You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

2 Días
0 Hrs
9 Min
0 Seg

Probando nuestros endpoints

21/27
Resources

How do you integrate validation middleware into the endpoints?

The use of validation middleware is fundamental to ensure data integrity and consistency in our applications. We have already created the entire structure of our middleware using Joy. Now, it is time to see how we apply them to each of our endpoints in an efficient way.

First, it is crucial to remember that we have created a function called ValidatorHandler. This is essentially a function that returns another function (thanks to JavaScript closures) and allows the creation of middleware dynamically. When using ValidatorHandler, we send a schema and specify the property containing the information needed to validate the data.

How to import and prepare the schemas?

The first thing we need to do is to import our ValidatorHandler function and the necessary schemas into our routes. This is essential since the routes are the ones that handle the request and response, and it is here where we will configure which schema to use for each endpoint.

const {ValidatorHandler} = require('middlewares');const {createProductSchema, updateProductSchema,  getProductSchema} = require('schemas');

How to apply middlewares to routes?

Each endpoint must run and define its own schema. The middleware will run on the route, accompanying other middleware or processes that each endpoint has configured. This ensures that validation is performed just before the service is executed.

For example, in an endpoint that receives an ID as a parameter, we must be sure that this ID complies with the expected format. This is how we apply it:

router.get('/products/:id', ValidatorHandler(getProductSchema, 'params'), (req, res) => { res.json({ message: 'Product successfully obtained' }); }) );

How to handle multiple validations?

In some cases, a route needs multiple validations. For example, an endpoint that requires validating both body and URL parameters. Middlewares are executed sequentially, so we can chain them together to verify each part of the data:

router.patch('/products/:id', ValidatorHandler(getProductSchema, 'params'), ValidatorHandler(updateProductSchema, 'body'), (req, res) => { res.json({ message: 'Product successfully updated' }); }) );

How to handle validation errors with Joy?

When a validation error occurs, it is important to provide a clear and specific response. Joy detects errors and returns an HTTP code 400 (Bad Request) if the validation fails. Initially, Joy only returns the first error it encounters. However, we can configure Joy to report all errors at once using the abortEarly: false option.

const options = { abortEarly: false };return schema.validate(data, options);

With this setting, the user receives all validation errors at once, improving the developer and end-user experience.

What basic functions should be taken care of in schemas?

  1. Field specificity: Clearly define mandatory fields and their types, such as a string or number.

  2. Value constraints: Set limits or rules, such as minimum and maximum length, or specific data type (e.g., alphanumeric).

  3. Field requirement: Determines if a field is mandatory using required.

  4. Valid URLs: Defines a field as a URL, if required.

This approach not only optimizes data integrity when interacting with our services, but also improves code structure and maintainability. By integrating validation middleware in each path, we ensure that invalid requests never reach the services in production, thus improving the robustness of our system.

Contributions 51

Questions 22

Sort by:

Want to see more contributions, questions and answers from the community?

Muy entendible todo. La verdad me encanta este curso hasta ahora.

Si te preguntas cuántas funciones middleware puedes enviar como callback, la respuesta es: las que quieras. Esto siempre y cuando las separes con coma. Las puedes llamar si las definiste fuera, ejecutar o incluso llamar un array de funciones middlewares.
Ejemplo de la documentación Express

const cb0 = function (req, res, next) {
  console.log('CB0')
  next()
}

const cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

app.get('/example/d', [cb0, cb1], function (req, res, next) {
  console.log('the response will be sent by the next function ...')
  next()
}, function (req, res) {
  res.send('Hello from D!')
})

Les comparto una forma de personalizar los mensajes de la validación en el productSchema.js Ejemplo si queremos los mensajes en español

const name = joi.string().min(3).max(15).messages({
  'string.base': `" nombre "debe ser un tipo de 'texto'`,
  'string.empty': `"nombre "no puede ser un campo vacío`,
  'string.min': `"nombre" debe tener una longitud mínima de {#limit}`,
  'string.max': `"nombre" debe tener una longitud máxima de {#limit}`
});

resultado

Si alguno ve este error en la consola:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Lo puede solucionar anteponiendo un return antes de los res. Esto sucede porque tenemos varios middlewares en los que se responde al cliente, y no se corta la ejecución de los mismos luego de enviado el primer res, por eso el error.

Quedaría así, (ejemplo para boomErrorHandler):

function boomErrorHandler(err, req, res, next) {
	if (err.isBoom) {
		const { output } = err;
		return res.status(output.statusCode).json(output.payload);
	}
	next(err);
}

Me ha gustado mucho el curso, el profe explica super claro todos los conceptos y practicar ayuda mucho a entender mejor lo conceptos, he estado desarrollando lo que hacemos para productos con los usuarios.


Asi sería mi Schema para los usuarios:

const Joi = require('joi');

const id = Joi.string().uuid();
const name = Joi.string().alphanum().min(3).max(15);
const lastname = Joi.string().alphanum().min(3).max(15);
const email = Joi.string().email();

const createUserSchema = Joi.object({
  name : name.required(),
  lastname : lastname.required(),
  email: email.required(),
});

const getUserSchema = Joi.object({
  id: id.required(),
});

module.exports = {createUserSchema, getUserSchema};
Y asi las ruta de usuarios:

const express = require("express");
const UserService = require("../services/userService");
const validatorHandler = require("../middlewares/validatorHandler");
const {createUserSchema, getUserSchema} = require("../schemas/userSchema");

const router = express.Router();
const service = new UserService();

router.get("/", async (req, res) =>{
  const users = await service.find();
  res.json(users);
});

router.post("/",
  validatorHandler(createUserSchema, "body"),
  async (req, res) =>{
    const body = req.body;
    const newUser = await service.create(body);
    res.status(201).json(newUser);
  }
);

router.delete("/:id", async (req, res) =>{
  const {id} = req.params;
  const answer = await service.delete(id);
  res.status(200).json(answer);
});

module.exports = router;

Consejo: pongan en las validaciones de price: .strict() ya que sino aceptará strings con números como “1000” y eso puede generarnos problemas después.

Este curso es increíble, hace la diferencia un buen profesor. Me han quedado claro conceptos que no había entendido bien en cursos anteriores.

Para quien tenga dudas con el código de porque el profe. puso, en validatorHandler.js

const data = req[property]; 

Se debe a que para obtener data dentro de un objeto se hace de dos formas.

const objeto = {
    name: "victor",
    number: 123
}
console.log(objeto.number)
const objeto = {
    name: "victor",
    number: 123
}
console.log(objeto["number"])

Profe, tome este curso porque en mi emprendimiento ya era necesario crear una API.

El curso es muuuuuuuuuuy bueno, has tomado la información relevante con clases cortas y concisas. De Verdad muchas gracias! me ha servido TODO

Muy buen curso, hasta el momento he entendido todo .
El tema de los middlewares lo habia escuchado pero recien ahora lo estoy empezando a entender.

Sin duda uno de los mejores cursos que me he encontrado en Platzi. Gracias Platzi, Gracias Nicolas Molina eres la mera ley… =)

Para realizar ciertas restricciones con joi y poder requerir al menos un valor por ejemplo que para actualizar un usuario, por lo menos en el body venga un valor pueden usar la siguiente estructura:

por si les llega a pasar

puse: con una coma al final y me falló la api

{
  "name": "Camisa",
}

lo puse así: y funcionó correctamente

{
  "name": "Camisa"
}

para que no acepte precios en formato texto ejemplo “price”: “1000” al validar hay que desactivar la opción por defecto que lo convierte a int.

const {error} = schema.validate(data,{convert: false});

Si quieren validar que el name sea requerido pero si este contiene espacios en blanco, solamente es omitir el alphanum().

const name = Joi.string().min(3).max(15);

Mi solución como reemplazo del alphanum:

const name = Joi.string().pattern(/^[a-zA-Z0-9 ]+$/).min(3).max(25);

De esta forma se valida que el contenido sea números o letras tanto en mayúscula como minúscula.

De la documentación de Joi:

number
Generates a schema object that matches a number data type (as well as strings that can be converted to numbers).

Por si alguien también se percató que se está aceptando price como number o string.

En el archivo

product.schema.js

Agregue .strict() para que al momento de hacer PATCH no pueda recibir, me di cuenta que “price” puede recibir numeros en una cadena de strings

{
"price": "2"
}

.

const price = Joi.number().integer().min(10).strict();

Ahora entiendo a las personas que dicen las validaciones deben ir en el back end

hasta ahora este curso de backend con nodejs me saco el sombrero, muy buena didactica en la enseñanza y comprension de la logica a compraracion de react, wepack, nextjs que si tenia muchos vacios y muchos errores

¿Por qué el profesor no hace esto?:

const updateProductSchema = Joi.object({
  name,
  price,
  image
})

está colocando los ‘:’ y en JS eso es innecesario si la clave tiene el mismo nombre que el valor

Está muy claro y práctico todo, genial el profe

Para los que no les funcione en el update y hagan la petición de que los campos son requeridos tienen que poner optional

const updateCategorySchema = Joi.object({
name : name.optional(),
image : image.optional(),
});

Otra forma de aplicar middlewares es

router.use('/:id', validatorHandler(getProductSchema, "params"))

Esto aplica el middleware a la ruta ‘/’ que le pasan por parametro el id

Áma veo middleware por todos lados.

products.route.js:

const express = require('express');
const ProductsService = require('../../services/product.service');
const validatorHandler = require('../../middlewares/validator.handler');
const {
  createProductSchema,
  updateProductSchema,
  getProductSchema,
  deleteProductSchema,
} = require('../../schemas/product.schema');

const router = express.Router();
const service = new ProductsService();

router.get('/', async (req, res) => {
  const products = await service.find();
  res.status(200).json(products);
});

router.get(
  '/:id',
  validatorHandler(getProductSchema, 'params'),
  async (req, res, next) => {
    try {
      const { id } = req.params;
      const product = await service.findOne(id);
      if (product) {
        res.status(200).json(product);
      } else {
        res.status(404).json({ message: 'Product not found' });
      }
    } catch (error) {
      next(error);
    }
  }
);

router.post(
  '/',
  validatorHandler(createProductSchema, 'body'),
  async (req, res) => {
    const body = req.body;
    const newProduct = await service.create(body);
    res.status(201).json({
      message: 'created',
      data: newProduct,
    });
  }
);

router.patch(
  '/:id',
  validatorHandler(getProductSchema, 'params'),
  validatorHandler(updateProductSchema, 'body'),
  async (req, res, next) => {
    try {
      const { id } = req.params;
      const body = req.body;
      const product = await service.update(id, body);
      res.status(200).json({
        message: 'updated',
        product,
      });
    } catch (error) {
      next(error);
    }
  }
);

router.delete(
  '/:id',
  validatorHandler(getProductSchema, 'params'),
  async (req, res) => {
    const { id } = req.params;
    const rta = await service.delete(id);
    res.status(200).json({
      message: 'deleted',
      rta,
    });
  }
);

module.exports = router;


Para el error en validatorHandler.js

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Debemos agregar el next(); después del if ya que sino hay error debe continuar al siguiente middleware y aparentemente en el video donde lo contruimos no aparece. Pero, si aparece en los recursos de este video.

const boom = require('@hapi/boom');

function validatorHandler(schema, property) {
  return (req, res, next) => {
    const data = req[property];
    const { error } = schema.validate(data, { abortEarly: false });
    if (error) {
      next(boom.badRequest(error));
    }
    next(); // __IMPORTANTE
  }
}

module.exports = validatorHandler;
eres un crack Nicolas!
Aunque Joi nos ayuda mucho aun faltan varias cosas para validar bien los datos.
El middleware que valida la creación de datos en Express.js, como se describe en la clase, utiliza un esquema definido con Joi. Este middleware, como el `ValidatorHandler`, se encarga de validar la información enviada en el cuerpo de la solicitud (body) antes de que los datos lleguen al servicio correspondiente. Cuando se implementa, primero se invoca el middleware de validación, que toma el esquema y verifica si los datos cumplen con los requisitos establecidos. Si los datos son válidos, el middleware llama a `next()`, permitiendo que la solicitud continúe; si no, se envía un error, normalmente con un código de estado 400 (Bad Request). Esto asegura que solo se procesen peticiones con datos correctos.
¿Alguien sabe si el curso está en Github??
Qué hermoso se ve validar todo de manera tan estructurada :')
Existe alguna forma de retornar el error de Joi pero en tipo json? es decir que se muestre un objeto de clave valor con el campo y el mensaje tipo { name: "es requerido" } por ejemplo
Si les sucede que el `req.body` para la petición `POST` siempre está `undefined` (me pasó que en el validador llegaba así siempre), deben instalar el paquete `body-parser` y agregar estas dos líneas al `index.js` (Similar a lo que hay que hacer para que express devuelva las respuestas en formato JSON) ```js const bodyParser = require('body-parser'); ... app.use(bodyParser.json()); ```const bodyParser = require('body-parser');
La libreria de Joi aplicaria cuando se esta trabajando con el modelo de base de datos ?

Es la segunda vez que veo el curso y ahora lo veo mucho más claro todo.

la mayoria de javascript ha estado escrita con el patron de diseño builder pattern, es donde siempre se retorna this, y permite hacer el codigo tan hermoso como el siguiente:

const name = Joi.string().alphanum().min(3).max(10);

Para las personas que no obtengan el error deseado, pueden revisar el orden en el que se ejecutan los middlewares de forma global en su archivo (index) o en su (app), si los tienen separados

Así es como defini mis esquemas de validación

Así es como definí mi validado:

Así es como apliqué las validaciones en el controlador/router

Así se ven mis resultados probados en postman

📑 Creo que una forma de manejar los valores a verificar es por un objeto y desestructuración, ya que permite actualizar la plantilla sin tener que repetir los valores no requeridos.

const Joi = require("joi");

//👇️

//--Plantilla de producto--
const defaultProductSchema = {
  id: Joi.string().uuid(),
  name: Joi.string().alphanum().min(2).max(20),
  price: Joi.number().integer().min(100),
  image: Joi.string().uri(),
}
//-------




//todos los datos requeridos (id no generado por el cliente si no por la API)
const createProductSchema = Joi.object({
  name: defaultProductSchema.name.required(),
  price: defaultProductSchema.price.required(),
  image: defaultProductSchema.image.required(),
})

//solo verificacion de ID en params
const findProductSchema = Joi.object({
  id: defaultProductSchema.id.required()
})

/*ID requerido pero sin embargo verifica todas las propiedades para verificar
cualquier cambio en cualquier propiedad*/
const updatedProductSchema = Joi.object({
  ...defaultProductSchema,
  id: defaultProductSchema.id.required()
})

const deleteProductSchema = Joi.object({
  id: defaultProductSchema.id.required()
})


module.exports = { createProductSchema, findProductSchema, updatedProductSchema, deleteProductSchema };

Este curso es genial

Connect validation middleware (Joi schema) with router

He implementado el validador en Typescript de la siguiente manera:

//validator.handler.ts

import { Request, Response } from 'express';
import boom from '@hapi/boom';
import Joi from "joi";


/** Joi validator middleware */
export function validatorHandler(schema: Joi.Schema, property: string) {

  return (req: Request, res: Response, next: (arg0?: Error) => void) => {
    const data = (<any>req)[property];
    const { error } = schema.validate(data);
    if(error) {
      next(boom.badRequest(error.message));
    }
    next();
  }

}


A las rutas les pasamos los validadores que deseamos antes de que entre a la lógica del producto en si. En el caso de post, tenemos un validador que importamos del schema, y un body (que es de donde sacaremos la información para validar). Nuestro validador es para crear un producto. Entonces tenemos: validadorHandler( tipoDeValidador, ‘dondeLoSacamos’). Luego continua la lógica.
Products.js

router.post('/',
  validatorHandler(createProductSchema, 'body'),
  async (req, res) => {
    const body = req.body;
    const newProduct = await service.create(body);
    res.status(201).json(newProduct);
});

En caso del patch tenemos 2 parametros y requerimos 2 validadores. Nuestro primer validador es de obtener el parámetro (ya que no requerimos realizar cambios a todo el body de nuestro producto, esa es la característica de patch). Y por otro lado, para realizar cambios en esos parametros (que serán inscriptos en el body)

router.patch('/:id',
  validatorHandler(getProductSchema, 'params'),
  validatorHandler(uptdateProductSchema, 'body'),
  async (req, res) => {
    try {
      const { id } = req.params;
      const body = req.body;
      const product = await service.update(id, body)
      res.json(product);
    } catch (error) {
      res.status(404).json({
        message: error.message
      })
    }
});

Validator.handler.js

function validatorHandler (schema, property){
  return (req, res, next) => {
    const data = req[property];

la propiedad ‘abortEarly: false’ nos permite determinar si enviar el mensaje de error al primer error que encontremos. Por default es verdadero.

    const { error } = schema.validate(data, { abortEarly: false });
    if (error){
      next(boom.badRequest(error));
    }
    next();
  }
}

Muy bien, ya puedo validar los datos!

Excelente el curso, todo muy claro y entendible.

Ahora si me quedo clarísimo como funciona el middleware, que bien que explica, cada detalle

Un curso increíble. Estoy entendiendo todo a detalle.

la verdad excelente, boom y joi de maravilla conjunto los middlewares es una hermosura utilizarlo 😄

Si quieren realizar validaciones mas complejas que las que trae Joi por defecto pueden usar regex, yo lo usé de la siguiente forma:

const Joi = require('joi');

const id = Joi.string().uuid();
const email = Joi.string().email();
const firstName = Joi
  .string()
  .min(2)
  .max(50)
  .regex(/^\w+(?:\s+\w+)*$/)
  .messages({
    "string.pattern.base": "First Name accepts alphabetic characters, numbers and spaces"
  });
const lastName = Joi
  .string()
  .min(2)
  .max(50)
  .regex(/^\w+(?:\s+\w+)*$/)
  .messages({
    "string.pattern.base": "Last Name accepts alphabetic characters, numbers and spaces"
  });
const photo = Joi.string().uri();

const getUserSchema = Joi.object({
  id: id.required()
});

const createUserSchema = Joi.object({
  email: email.required(),
  firstName: firstName.required(),
  lastName: lastName.required(),
  photo: photo
});

const updateUserSchema = Joi.object({
  firstName: firstName,
  lastName: lastName,
  photo: photo
});

module.exports = { getUserSchema, createUserSchema, updateUserSchema };

hasta este video desde hace 3 videos atras llevo preguntandome que pasa si quiero que un middleware solo se ejecute en una sola ruta, me parece que se deberia haber explicado en el primer punto de que son los middleware...