Curso de Backend con NodeJs: API REST con express
link del repositorioServicios
En los servicios es donde empezamos a encapsular toda la logica del negocio, o Aplicacion.

The Clean architecture es una arquitectura definida por capas:
-
Entidades: Es donde encontramos las entidades base de nuestra arquitectura, como entidad de productos, categorias, usuarios.
-
Casos de Uso: Es donde tenemos todo lo relacionado a la logica de negocio. es donde se encuentran los Servicios.
Controladores: Estos son los que brindan acceso, es donde encontramos a todo el Routing

Los controladores donde encontramos el routing y los middlewares, luego => tenemos a los servicios, donde se encuentra toda la logica de negocio y son los que usan a las librerias, => las librerias se encargan de conectarse a esta capa de entidades y a otras fuentes de datos como API externas o otras bases de datos
Separando la logica del negocio, implementando los Servicios
- Empezaremos por crear carpeta para los servicios
- Creamos nuestro servicio: productServices.js, en este momento empezamos con la programacion orientada a objetos (POO)
- Empezamos a definir toda la logica a nivel transaccional que van a tener nuestros datos, aquí vamos a gestionar todo lo de los productos como crear, editar, etc, entonces vamos a implementar esas funcionalidades.
- Creamos las funcionalidades:
-
Cortamos y pegamos la funcion donde utilizamos faker para generar productos falsos y llenamos los campos de los metodos:
-
En el Router nos traemos el servicio con las siguientes lineas de codigo(vamos a requerir el servicio de productos, luego creamos la instancia de la clase y finalmente establecemos el route donde indicamos que nos traiga el array de productos que creamos en la clase ProductsService):
-
creamos el atributo id (dentro de generate, dentro del this.products.push({})), y le decimos que cree un string muy largo de forma randomica (uuid()):
id: faker.datatype.uuid(),
luego en el metodo findOne(), le agregamos el parametro id y manipulamos los array para que encuentre el item id y le de el valor de id. -
Ahora en el Routing vamos a cambiar la ruta que recibe el parametro id, eliminamos el codigo anterior y vamos a escribir este nuevo codigo, un poco más inteligente.
-
finalmente podemos probar en el cliente nuestra aplicacion, vamos a ver que tiene un id y que el id si lo buscamos en el url va a encontrar el producto por su id, además, como esta vez utilizamos el constructor, los productos quedan guardados en memoria RAM
Create, Update & delete
Vamos a crear, actualizar y eliminar utilizando la capa de servicios, con la ventaja que ya tenemos un tipo de persistencia, que es la persistencia en memoria.
Create
-
ingresamos el parametro data y las instrucciones que necesita nuestro metodo create.
-
Vamos a nuestro post de productsRouting.js y lo reconfiguramos para que ejecute el metodo create, con la data que le enviamos y para que cree un id automaticamente.
-
probamos que funcione llendonos a post y creando el producto, vamos a crear un producto que cuesta 500
-
verificamos que se halla creado viendo en la lista de productos, antes de eso le volvemos a hacer request/send para que se actualize la lista.
Update/Actualizar
-
ingresamos los parametros id, changes y las instrucciones que necesita nuestro metodo create.
-
Luego en el routing configuramos para que utilice la capa de servicios:
-
Pero al hacer la prueba resulta que al actualizar el producto elimina el resto de la informacion, dejando solo la informacion que actualizamos:
-
Vamos a corregir esto, adicionando el siguiente codigo:
-
volvemos a probar:
Delete
-
Agrego la siguite linea de codigo:
-
reconfiguramos en el routing para que utilice los servicios
-
probamos que el producto se elimine en el listado que tenemos:
verificamos existencia:
eliminamos el producto:
Async, await y captura de errores
Hasta ahora hemos corrido Node de forma syncrona, y es bueno pero esto en la web o en la coneccion con bases de Datos puede ser un problema debido al delay(demora) de coneccion. Entonces debemos saber como manejar asyncronismo en los servicios.
Vamos a añadir el async a todos los metodos, por el momento los vamos a agregar sin el await pero en el routing si vamos a usar el async y await de forma completa. lo vamos a dejar así para tenerlo preparado en el futuro.


Captura de errores:

Spread
SintáxisLa sintaxis extendida o spread syntax permite a un elemento iterable tal como un arreglo o cadena ser expandido en lugares donde cero o más argumentos (para llamadas de función) o elementos (para Array literales) son esperados, o a un objeto ser expandido en lugares donde cero o más pares de valores clave (para literales Tipo Objeto) son esperados.
sintáxis SpreadMiddlewares
Middleware es software que permite uno o más tipos de comunicación o conectividad entre dos o más aplicaciones o componentes de aplicaciones en una red distribuida. Al facilitar la conexión de aplicaciones que no fueron diseñadas para conectarse entre sí, y al brindar funcionalidad para conectarlas de manera inteligente, el middleware agiliza el desarrollo de aplicaciones y acelera el tiempo de comercialización.
Estos estan en medio del request y el response, osea que cada vez que por ejemplo en routing en el momento del callback, recibimos un request y un response, esto quiere decir que casi todo lo que colocamos en ese callback, es un middleware

Pueden funcionar de forma secuencial, quiere decir que yo tengo varios middlewares y despues corra otro y hacer una cadena de middlewares, por ejemplo: el primer valide logica de autentificacion, en el segundo podria validar la data, en el tercero ahcer otra cosa

esto quiere decir que uno de estos middlewares puede bloquear la ejecucion de esta otra, por ejemplo si ese usuario en especifico no tuviera el permiso necesario para aceder a esa informacion:

Esta son la clase más común de middlewares, pero hay otro tipo de middlewares que se ejecutan de otras formas:


Tenemos los siguientes casos de uso

¿Que es un pipe? Las pipes también llamadas tuberías o filtros son funciones que se llaman en una vista (html) y que tienen por objetivo transformar un dato a mostrar para mejorar la experiencia del usuario.
Middleware for errors(httpErrors)
vamos a crear un middleware que actue de forma global, para que capture los errores y formatearlo de una manera adecuada para devolverlo a nuestro cliente¿como haremos el middleware global para errores?
- Vamos a crear un nueva carpeta y un nuevo archivo llamado midlewares y errorHandler.js
-
Creamos la funcion con la que vamos a logear el error, Es importante acotar que en los parametros agregemos el err, y el next y al final ejecutemos la funcion next enviandole el err como parametro para que Node entienda que estamos ejecutando un middleware de tipo error.
-
Creamos otra funcion que va a crear y enviar el formato para que le enviemos el error a nuestro cliente:
Recuerda que aún así no estes utilizando next, tiene que agregarlo como parametro para que Node entienda que estas trabajando con un Middleware de correccion de Errores
-
Vamos a importar nuestras funciones al index.js
Los Middlewares del tipo error se deben crear despues de haber establecido el routing de nuestra aplicacion
-
Le decimos a la aplicacion que use los middlewares, tenemos que tener mucha delicadeza con el orden en que llamamos a los middlewares, para que se ejecuten en el orden de cadena correcto, ya que en el orden que se ejecuten las lineas sera el orden en el que se ejecuten uno de tras del otro
-
Finalmente debemos tambien capturar el error de una forma explicita desde el routing:
Establecemos a next tambien como parametro de la funcion, utilizamos un try y catch para caprturar el error y finalmente declaramos la funcion next(error) para que los middlewares ejecuten
lo hemos hecho bien, con excepcion que estamos incurriendo en una mala practica al enviar el error 500, cuando deberiamos utilizar otra clase de status code D: :0
Manejo de errores con Boom
Nos va a permitir hacer el manejo o manipulacion de errores pero respetando el status code
¿Como implementarla?
-
Ejecutamos el comando npm para instalar Boom
npm i @hapi/boom
-
Como lo vamos a necesitar en nuestro archivo de servicio, vamos a requerir la dependencia en el archivo productService.js
const boom = require('@hapi/boom');
-
cambiamos nuestra forma de manipular los errores dentro del codigo.
-
Tenemos que crear un middleware ya que boom maneja los errores de una forma diferente, para esto nos vamos al archivo de middleware errors, aprovechando que cuando el sistema genera un error con boom automaticamente le agrega una propiedad llamada isBoom
si no es un error del tipo boom ejecutara el siguiente middleware
-
vamos a importar la libreria tambien en el routing para ejecutarla
const { logErrors, erroHandler, boomErrorHandler } = require('...DIreccion/archivoMiddlewareforErrors');
-
Ahora lo empezamos a usar dentro de la cadena de middlewares:
-
vamos a probar enviando la peticion de un producto que no existe:
-
Podemos crear un nuevo atributo de producto para bloquear algunos productos y otros no de forma random y bloquear algunos productos y otros no
-
Agregando un error de logica de negocio, tipo conflicto
-
Excelente ahora podemos consultar por un producto que si exista pero que se encuentre bloqueado
Destructuring
es destructurando el objeto o sacando solo que necesitamos de el, ya que lo anterior nos retorna un objeto con {} de decimos que solo nos traiga la propiedad error del objeto.
es una forma de acceder al valor o atributo de un objeto, por ejemplo podemos eso decir:
que tenemos un objeto con el siguiente valor: { value: {}, error: '"username" is required' }
const {error} = schema.validate(data);
Es lo mismo que decir:
const error = schema.validate(data).error;
Validación de Datos
Middlewares para validar los datos, validar los datos que me estan enviando desde el cliente,si yo tengo una bibliotecta ya sea angular o react, ellos nos envian la información a la API, pero nosotros como Backend deberiamos decirle que cumple con la integridad de datos requerida para poder insertar eso a una base de datos.
Vamos a ver como podemos hacer esta validacion de datos, para esto vamos a utilizar una libreria llamada joi
-
Empezamos por instalar la libreria Joi
npm i joi
-
creamos la carpeta de schemas.
Podemos llamarlos schemas o tambien podemos llamarlo Dtos
- creamos el archivo productSchemas.js
- importamos la libreria con el require
-
Haremos un schema especidfico para cada campo, eso nos va a ayudar a reutilizar mucho mejor el codigo y para poder reutilizar ese codigo, ademas que simplifica la creacion.
Debemos empezar por el tipo de campo que es por ejemplo:(string()) y luego la validacion:(uuid())
-
escribimos nuestro codigo para los metodos y le indicamos cuales son requeridos y cuales no....
-
Vamos a crear un middleware para realizar estas validaciones, empezaremos por crear en la carpeta de middlewares el archivo validatorHandler.js
-
esto ya seria un middleware normal que solo tendria los parametros: req, res, next u otros.
vamos a configurar un middleware que sea dinamico, por eso vamos a recibir el schema y la propiedad, luego vamos a crear un closure o dicho de otra forma vamos a retornar una funcion:
ya hemos creado toda la esturctura de nuestro middleware de validacion de datos con joi, ahora vamos a aprender a aplicarlo y ver como se Utiliza:
Vamos a ver como se utiliza
- vamos a las rutas donde estan los metodos de request y response. Abrimos productRouter.js
-
importamos a validatorHandler y a productSchemas
-
Antes de ejecutar el middleware asyncrono con el que conectamos al servicio, vamos a ejecutar los middlewares de validacion de datos, si todo sale bien correra el next y ejecutara el middleware de servicio o routing
Dentro de los parametros como habiamos puesto en el validatorhandler le indicamos que schema vamos a usar en dichos flujos y donde se encuentra la informacion(la propiedad)
¿que tal si tengo más propiedades y me toca agregarla a las dos? simplemente lo concatenamos o ejecutamos el siguiente middleware
-
Podriamos probar nuestra validacion y vamos a ver que si enviamos a crear un producto con un nombre de más de un caracter nos muestra un error, eso fue por que nosotros lo programamos para que acepte solo nombres alfa-numericos
-
Le vamos a eliminar la validacion de caracter alfa numerico y vamos a crear una nueva constante para tambien validar imagenes en productSchema
-
Vamos a poner en el metodo de crear productos que la imagen sea requerida pero no para actualizar los productos. En productSchema
-
El otro problema es que cuando hay más de un error, nos imprime un solo error de validacion, esto es molesto y frustrante para nuestris clientes, entonces vamos a configurar a Joi para que impriman todos los errores de validacion de golpe y el cliente sepa todo lo que tiene que corregir antes de volver a enviar una peticion
Lo haremos adicionando al validate un objeto que le diga al atributo abortEarly que no aborte cuanto antes, que aborte despues de verificar todos los datos
-
Y listo ya valida todo en conjunto y de forma correcta
Middlewares populares en Express.js
A continuación te compartiré una lista de los middlewares más populares en Express.
CORS
Middleware para habilitar CORS (Cross-origin resource sharing) en nuestras rutas o aplicación. http://expressjs.com/en/resources/middleware/cors.html
Morgan
Un logger de solicitudes HTTP para Node.js. http://expressjs.com/en/resources/middleware/morgan.html
Helmet
Helmet nos ayuda a proteger nuestras aplicaciones Express configurando varios encabezados HTTP. ¡No es a prueba de balas de plata, pero puede ayudar! https://github.com/helmetjs/helmet
Express Debug
Nos permite hacer debugging de nuestras aplicaciones en Express mediante el uso de un toolbar en la pagina cuando las estamos desarrollando. https://github.com/devoidfury/express-debug
Express Slash
Este middleware nos permite evitar preocuparnos por escribir las rutas con o sin slash al final de ellas. https://github.com/ericf/express-slash
Passport
Passport es un middleware que nos permite establecer diferentes estrategias de autenticación a nuestras aplicaciones. https://github.com/jaredhanson/passport
Puedes encontrar más middlewares populares en el siguiente enlace: http://expressjs.com/en/resources/middleware.html
Produccion
Estamos a punto de llevar nuestro sitio a produccion, pero hay que tomar en cuenta unas cuantas consideraciones.... Como el CORS=>Cross-Origin Resource Sharing

-
Evaluar los CORS:
a quienes les damos acceso para atender solicitudes
-
Que nuestra API este sobre un servidor que sea https
por que esta cifrado, en cambio en http cualquiera podria tener acceso a nuestra informacion de una manera sencilla
-
Tener procesos de BUILD
esto tiene más que ver con cosas que tienen que procesar una informacion, por ejemplo typescript que tiene que pasarse a javascript o sass... si tienes algun proceso de building deberias trabajarlo y verificar que funcione bien antes de ponerlo a trabajar en produccion
-
Remover Logs
No es bueno tener logs, no es bueno tener logs en produccion, incluso esto aveces puede hacer demorar la aplicacion, se puede utilizar sarcentry o datalog para capturar todo esto en produccion
-
Seguridad
Es importantisimo trabajar la seguridad desde el principio, Helmet es una colleccion de middlewares que ya ponen algunas capas de seguridad a tu aplicacion de Node
-
Testing: Pruebas Unitarias
tenemos que hacer pruebas unitarias o pruebas de integracion antes de subir nuestra aplicacion
CORS
hasta ahora hemos probado haciendo peticiones desde el mismo origen, y por eso no habia problema con recibir las peticiones hechas, pero que tal si la peticion viene desde un origen diferente? el servidor automaticamente las va a rechazar

Pero que tal si necesitamos empezar a usar subdominios, otros dominios e incluso applicaciones mobile => android o !os? son applicaciones que tienen un origen diferente

Por defecto nuestro Backend rechazara estas peticiones de origenes diferentes
entonces... ¿como configuramos nuestros puertos u origenes de acceso para los dominios?
-
Sí, hay un modulo llamado CORS para dar esta solucion, y lo instalamos con el comando:
npm i cors
Esta libreria funciona como un middleware
-
Lo requerimos en el index donde esta el routing y vamos a configurar, const cors = require('cors');
-
y luego lo ejecutamos:
app.use(cors());
Pero si lo dejamos así, la applicacion aceptaria cualquier origen
-
entonces creamos un array como nuestra whitelist con nuestros dominios y origenes permitidos, además una constante llamado options con un objeto que tenga un atributo origin que ejecute una funcion flecha o arrow function que ejecute una validacion que vea si el origen esta incluido en el whitelist, y que en caso de no serlo lanze un error
no olvidemos de enviar como parametro al atributo options
- aunque siento que no me quedo muy claro eso seria todo
Deployment
Vamos a desplegar nuestro sitioweb dentro de los servidores de Heroku
-
instalamos Heroku
Si estamos en linux nos pueden servir y nos es recomendado los siguientes comandos:
curl https://cli-assets.heroku.com/install.sh | sh
heroku login
heroku create
git remote -v
Si no tambien podemos ingresar a la pagina oficial, registrarnos o logiarnos e ir a la documentacion oficial para instalar el asistenete de comandos
-
ya habiendo ingresado a Heroku,
le damos clic a documentation y luego a ger start.
FInalmente le das click o copias el comando que te dan, este caso hare click en el boton de windows 64-bit installer
Si estas en ubuntu puedes instalarlo con el siguiente comando
sudo snap install --classic heroku
Si estas en macOS puedes instalarlo con el siguiente comando
brew tap heroku/brew && brew install heroku
-
luego de haber instalado heroku en nuestros sistemas, y hecho loggin(aunque no importa por que te va a pedir que des click en un link para loggearte autoamticamente) vamos a la carpeta de la aplicacion y ejecutamos el siguiente comando:
heroku create
-
Despues de haber hecho esto, Heroku va a crear un repositorio de Git, lo puedes verificar ejecutando el comando:
git remote -v
Con esto vamos a poder ir haciendo commits y automaticamente va a haber un deployment por que como viste ahora hay dos origenes, tambien nos da una url de nuestro proyecto
https://frozen-eyrie-22132.herokuapp.com/
y si vas a tu dhasboard de heroku podras ver tu app
-
como dice en este link En nuestro package.json debemos especificar que version de node queremos correr en neustra aplicacion, agregando la siguiente linea de codigo
-
Podemos correr en un entorno de heroku local desde nuestra compu antes de problarlo de forma remota en el servidor con el comando:
heroku local web
-
hacemos cambios en los CORS de nuestro sitio y en el puerto para que se asigne un puerto de forma automamtica o que escoga un puerto especifico 3000 y a la vez para que acepte las peticiones del puerto local
-
dentro de los archivos de nuestro proyecto creamos el archivo procfile, este archivo no va a llenar ningun punto ni formato por que ya lo coge por defecto, en el cual le vamos a indicar como queremos que heroku lance el proyecto, seria lo mismo de especificarle el comando que debemos darle para que corrra o el script
en este caso le especificaremos que corra el script npm run start, que en nuestro pakage.json es node index.js
con este comando correremos nuestra aplicacion directamente a produccion
-
Llego el momento esperado, ya podemos hacer nuestro deploy y lo haremos haciendo un push a nuestro repositorio de heroku, así automaticamente se hara el deployment debido
git push heroku main
-
bien, al correr exitosamente el comando anterior, ya esta hecho el deployment, y te envia un link con el dominio de tu aplicacion, si gustas puedes despues ir a settings de tu app en el dashboard de heroku y prosonalizar tu propio dominio.
En este caso nuestro dominio es: https://vast-temple-37338.herokuapp.com/ entramos y guala!!! tenemos nuestra aplicacion corriendo exitosamente