No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Variables de ambiente en Node.js

8/27
Recursos

Aportes 28

Preguntas 20

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Unit Test

💡Es el proceso de ejecutar un programa con la meta de encontrar errores. Si la “prueba” es exitosa, entonces podemos asegurar que, alguna característica o funcionalidad, fue cubierta.

.
Para este caso, sugiero que implementen sus pruebas ya sea de unit test (función o módulo) y/o end-to-end (integración).
.

Configuración

ℹ️Repositorio: link
.
Para adicionar pruebas a nuestro código utilizamos:

  • jest. Engine de pruebas para JavaScript
  • supertes. Handler Process para peticiones HTTP.

.
✨Como retornamos un arreglo como data para validar el cambio de pool podemos definir el siguiente bloque:

import supertest from 'supertest';
import app from '../src/index';

const request = supertest(app);

/**
 * @description colleciton of test cases on task request
 * @param {string} - case name
 */
describe('routes', () => {
    describe('GET /task', () => {
        it('should respond with a json', async () => {
            const { status, body: response } = await request.get('/task');
            expect(status).toBe(200);
            expect(Array.isArray(response.data)).toBeTruthy();
        });
    });
});

♻️Ambas librerías para pruebas, necesitamos obtener un servidor http y express no los retorna cuando utilizamos express().

import express from 'express';
import task from './routes/api/task';

// code
const app = express();

// code
app.use('/task', task);

// code
if (mode !== 'test')
    app.listen(port, () => listen(`⬢ Server Thingst - ${mode}`));
export default app;

🔥Evitamos que en test no levante el servidor, ya que supertest lo hará por nosotros.
.
♻️Actualizamos nuestro script para leer nuestros test:

"test": "NODE_ENV=test jest test/index.test.js --forceExit",

¿Recuerdan el documento docker-compose.yml?
Pues resulta que también le podemos colocar las variables de entorno de la siguiente manera, sin configuraciones o paquetes adicionales:

version: "3.3"

services:
  postgres:
    image: postgres:13
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    ports:
      - ${DB_PORT}:${DB_PORT}
    volumes:
      - /postgres_data:/var/lib/postgresql/data

  pgadmin:
    image: dpage/pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=${PG_EMAIL}
      - PGADMIN_DEFAULT_PASSWORD=${PG_PASSWORD}
    ports:
      - ${PG_PORT}:${PG_REFPORT}

Hagan la prueba y me cuentan si funciona. 😉

Variables de ambiente en Node.js

Las variables de entorno se utilizan para contener contenido sensible

Instalamos el siguiente paquete

Esto nos sirve para tener las variables de entorno corriendo en el proceso de node

npm i dotenv

Creamos nuestro archivo de configuracion

config>config.js

require('dotenv').config();

const config = {
  env: process.env.NODE_ENV || 'dev',
  port: process.env.PORT || 3000,
  dbUser: process.env.DB_USER,
  dbPassword: process.env.DB_PASSWORD,
  dbHost: process.env.DB_HOST,
  dbName: process.env.DB_NAME,
  dbPort: process.env.DB_PORT,
};

module.exports = { config };

Adaptamos nuestro pool

Archivo anterior

const { Pool } = require('pg');

const pool = new Pool({
  host: 'localhost',
  port: 5432,
  user: 'kevin',
  password: 'admin123',
  database: 'my_store',
});

module.exports = pool;

Archivo usando las variables de entorno

La URI se conforma por “protocolo😕/USUARIO:CONTRASEÑA@HOST:PUERTO/NOMBREDB

El encodeURI es una manera de proteger estos datos tan sensibles, lo mismo cuando ya tenemos la URI armada

const { Pool } = require('pg');
const { config } = require('../config/config');

const USER = encodeURIComponent(config.dbUser);
const PASSWORD = encodeURIComponent(config.dbPassword);
const URI = `postgres://${USER}:${PASSWORD}@${config.dbHost}:${config.dbPort}/${config.dbName}`;

const pool = new Pool({ connectionString: URI });

module.exports = pool;

Archivo .env

	PORT=3000
  DB_USER='kevin'
  DB_PASSWORD='admin123'
  DB_HOST='localhost'
  DB_NAME='my_store'
  DB_PORT='5432'

Archivo .env.example

	PORT='',
  DB_USER='',
  DB_PASSWORD'',
  DB_HOST'',
  DB_NAME='',
  DB_PORT='',

Aplicando desestructuración de objetos podemos evitar repetir código.

Les dejo el archivo config.js por si no quieren escribir 😃


const config = {
	env: process.env.NODE_ENV || 'dev',
	port: process.env.PORT || 3000,
	dbUser: process.env.DB_USER,
	dbPassword: process.env.DB_PASSWORD,
	dbHost: process.env.DB_HOST,
	dbPort: process.env.DB_PORT,
	dbName: process.env.DB_NAME,
};

module.exports = { config };

const URI = `postgres://${USER}:${PASSWORD}@${config.dbHost}:${config.dbPort}/${config.dbName}`;

utilize esta extension para que se vea bonito el archivo .env
DotENV

un pequeño aporte que quizas le sirva a alguno, en las variables de entorno, en el archivo .env, no se debe poner coma al final. La coma hace que node tome la variable como si la coma fuera parte de la asignación y claramente genera error

<code> 
PORT = 3000
DB_USER='juan'
DB_PASSWORD='admin123'
DB_HOST='localhost'
DB_NAME='my_store'
DB_PORT='5432'

Si alguien movio su archivo .env de la carpeta raiz es posible que les tire este error al momento de hacer peticiones:

node:internal/process/promises:279
            triggerUncaughtException(err, true /* fromPromise */);
            ^

Error: getaddrinfo ENOTFOUND undefined
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:109:26) {
  errno: -3008,
  code: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'undefined'
}

De ser asi, con regresar el archivo .env a la carpeta raiz debe volver a funcionarles correctamente.

Me siento como si estuviese construyendo Laravel desde cero pero con JavaScript. Un Taylor Otwell.

Este man sabe mucho

A alguien mas el dotenv le crea un archivo con el puerto?

Por si alguien no lo sabe, para que las variables de entorno tengan efecto en el código, es necesario reiniciar el servidor de node.js.

En config.js se tiene la configuración base para leer las variables de entorno.

En node se leen las variables de entorno con process.env.NODE_ENV seguido del entorno process.env.NODE_ENV || ‘dev’.

require('dotenv').config();

const config = {
  env: process.env.NODE_ENV || 'dev',
  port: process.env.PORT || 3000,
  dbUser: process.env.DB_USER,
  dbPassword: process.env.DB_PASSWORD,
  dbHost: process.env.DB_HOST,
  dbName: process.env.DB_NAME,
  dbPort: process.env.DB_PORT,
};

module.exports = { config };

En el archivo postgres.pool.js se trae la configuración, una configuración es no enviar variable por variable, más bien, protegerlas un poco usando encodeURIComponent() y mandar una URI con todo el esquema de conexión.

const { Pool } = require('pg');
const { config } = require('../config/config');

const USER = encodeURIComponent(config.dbUser);
const PASSWORD = encodeURIComponent(config.dbPassword);
const URI = `postgres://${USER}:${PASSWORD}@${config.dbHost}:${config.dbPort}/${config.dbName}`;

const pool = new Pool({ connectionString: URI });

module.exports = pool;

En el ambiente de desarrollo, se crea un archivo de variables de entorno .env (archivo delicado, no se sube a github), ahí se indican los datos sensibles de la BD u otras configuraciones de ejecución. Una buena practica es tener un archivo .env.example el cual indica un ejemplo de cómo deberían ir las variables de entorno.

La librería dotenv nos ayuda a leer el archivo .env . Para usarla, primero se requiere en el archivo config.js y lo que hace por defecto es leer el archivo .env y cargarlas al proceso de node.

dotenv ya no es necesario:
Para las versiones más recientes de Node (20.6.0 y posteriores) ya no es necesaria la librería dotenv. Puedes simplemente agregar la flag --env-file=.env a los scripts

"dev": "node --watch --env-file=.env index.js",
"start": "node --env-file=.env index.js"

Nota: Desde la versión 18.11.0 se agregó a Node el modo watch como una feature experimental, por lo que, de momento, Nodemon tampoco es necesario. Agregando la flag --watch puedes activar el modo.

Para los que usan ES modules y no CommonJS ```js import pkg from 'pg'; import dotenv from 'dotenv'; // Cargar las variables de entorno desde el archivo .env dotenv.config(); const { Pool } = pkg; // Configurar el pool de conexiones de PostgreSQL usando las variables de entorno export const pool = new Pool({ user: process.env.DB_USER, password: process.env.DB_PASSWORD, host: process.env.DB_HOST, database: process.env.DB_NAME, port: process.env.DB_PORT, }); ```
Les recomiendo que si tienen mucho problema con el docker, es decir, con los errores es mejor utilizar el mismo PostgreSQL instalado en el sistema, crear una base de datos y conectarse a ella de forma local para no estar como yo que en 3dias estuve buscando un error para solucionarlo y no tener lograr solucionar

lo más recomendado es que a su archivo postgres.js también utilicen las variables de entorno, les dejo la forma en que yo lo hice:

const { Client } = require('pg');
const { config } = require('./../config/config');

const USER = encodeURIComponent(config.dbUser);
const PASSWORD = encodeURIComponent(config.dbPassword);
const HOST = encodeURIComponent(config.dbHost);
const DATABASE = encodeURIComponent(config.dbName);
const PORT = encodeURIComponent(config.dbPort);
const URI = `postgres://${USER}:${PASSWORD}@${HOST}:${PORT}/${DATABASE}`;

async function getConnection() {
    const client = new Client({ connectionString: URI});
    await client.connect();
    return client;
}

module.exports = getConnection;

La encodeURIComponent()función codifica un URI reemplazando cada instancia de ciertos caracteres por una, dos, tres o cuatro secuencias de escape que representan la codificación UTF-8 del carácter (solo habrá cuatro secuencias de escape para caracteres compuestos por dos caracteres “sustitutos”).

Hola comunidad un saludo, Desde la versión 20.6.x en adelante ya no hace falta instalar la dependencia de **dotenv**, en el package.json se pasa el ***.env*** como parámetro. `"scripts": {` ` "dev": "node --env-file .env index.js"` `}` **Nodemon** tampoco es necesario desde la versión 22.x ahora podemos usar ***--watch*** `"scripts": {` ` "dev": "node --watch --env-file .env index.js"` `}`
me di cuenta del error antes que el profe, algo debo estar haciendo bien jjaja

El URI identifica un recurso y lo diferencia de otros mediante un nombre, una ubicación o ambos. La URL identifica la dirección web o la ubicación de un recurso único.

encodeURI y encodeURIComponent Las URL solo pueden tener ciertos caracteres de los 128 set de caracteres estándar ASCII. Se debe codificar los caracteres reservados que no pertenezcan a este set. Los caracteres especiales admitidos por UTF-8. Esto significa que es necesario codificar estos caracteres especiales cuando pasan a una URL. Cuando los caracteres especiales como &, space, á, é, í, ó, ú, ñ, ü, etc. entran en una URL, se tienen que reemplazar mediante la codificación, ya que si no, pueden causar situaciones impredecibles.

Resumen: Si tienes una URL completa, utiliza encodeURI. Pero si tienes una parte de una URL, utiliza encodeURIComponent.

Si estan usando Mysql quedaria algo asi.const { Sequelize } = require('sequelize');const { config } = require('../config/environment'); const sequelize = new Sequelize(config.DB\_NAME, config.DB\_USER, config.DB\_PASSWORD, {  host: config.DB\_HOST,  port: config.DB\_PORT,  dialect: 'mysql',  logging: false,  pool: {    max: 5,    min: 0,    acquire: 30000,    idle: 10000  }}); *module*.*exports* = { sequelize } ```js const { Sequelize } = require('sequelize'); const { config } = require('../config/environment'); const sequelize = new Sequelize(config.DB_NAME, config.DB_USER, config.DB_PASSWORD, { host: config.DB_HOST, port: config.DB_PORT, dialect: 'mysql', logging: false, pool: { max: 5, min: 0, acquire: 30000, idle: 10000 } }); module.exports = { sequelize } ```

Tambien puedes guardar las variables de docker-compose en el archivo .env y asi compartir las credencias en el docker y en express sin tener que duplicar las credencias ni dejarlas en el codigo:

version: '3.3'

services:
  postgres:
    container_name: db.henryjperez
    image: postgres
    env_file:
      - .env
    ports:
      - "${POSTGRES_PORT}:${POSTGRES_PORT}"
    volumes:
      - ./db_data:/var/lib/postgresql/data

  pgadmin:
    container_name: pgadmin
    depends_on:
      - postgres
    image: dpage/pgadmin4
    env_file:
      - .env
    ports:
      - 5050:80

Parte del config.js:

// Postgres
export const postgres_db = process.env.POSTGRES_DB;
export const postgres_user = process.env.POSTGRES_USER;
export const postgres_password = process.env.POSTGRES_PASSWORD;
export const postgres_port = process.env.POSTGRES_PORT;
export const postgres_host = process.env.POSTGRES_HOST;

paquete dot env: para leer las variables de entorno

instalamos dependencias

npm install dotenv 

creamos variables de ambiente y variables de ambiente de ejemplo
.env:

PORT=3000
DB_USER='jclentino'
DB_PASSWORD='toor'
DB_HOST='localhost'
DB_NAME='my-store'
DB_PORT='5432'

.env.example:

PORT=3000
DB_USER=''
DB_PASSWORD=''
DB_HOST=''
DB_NAME=''
DB_PORT=''

creamos un objeto de configuración con las variables de entorno
config/config.js:

require('dotenv').config()

const config = {
  env: process.env.NODE_ENV || 'dev',
  port: process.env.PORT,
  dbUser: process.env.DB_USER,
  dbPassword: process.env.DB_PASSWORD,
  dbHost: process.env.DB_HOST,
  dbName: process.env.DB_NAME,
  dbPort: process.env.DB_PORT,
}

module.exports = { config }

importamos el objeto de configuración en el archivo que contiene el pool de conexiones y reemplazamos la conexión por un string de conexión
libs/postgres.pool.js:

const { Pool } = require('pg')
const { config } = require('../config/config')

const USER = encodeURIComponent(config.dbUser)
const PASSWORD = encodeURIComponent(config.dbPassword)
const URI = `postgres://${USER}:${PASSWORD}@${config.dbHost}:${config.dbPort}/${config.dbName}`

const pool = new Pool({ connectionString: URI })

module.exports = pool

corremos el proyecto

npm run dev 

si visitamos la url localhost:3000/api/v1/products/ veremos como respuesta los registros en la tabla tasks

[
  {
    "id": 1,
    "title": "Terminar curso de postgres ",
    "completed": false
  },
  {
    "id": 2,
    "title": "Limpiar ",
    "completed": false
  }
]

hola, si a alguien le aparece un error Error: getaddrinfo ENOTFOUND traten de revisar la URI o la sintaxis de nuevo
o fíjense de que {config} este desestructurado XD

Solo como comentario, crear un carpeta para un solo archivo se me hace algo innecesario, de igual forma al crearla me parece sería mejor nombrar el archivo como index.js asi los imports quedan de la misma maneta que si solo crearas el archivo config.js en raíz y son igualmente legibles y ordenados.