¿Cómo integras los middlewares de validación en los endpoints?
El uso de middlewares de validación es fundamental para asegurar la integridad y consistencia de los datos en nuestras aplicaciones. Ya hemos creado toda la estructura de nuestros middlewares usando Joy. Ahora, es momento de ver cómo los aplicamos en cada uno de nuestros endpoints de manera eficiente.
Primero, es crucial recordar que hemos creado una función denominada ValidatorHandler. Esta es esencialmente una función que retorna otra función (gracias a los closures de JavaScript) y permite la creación de un middleware de forma dinámica. Al utilizar ValidatorHandler, enviamos un esquema y especificamos la propiedad que contiene la información necesaria para validar los datos.
¿Cómo importar y preparar los esquemas?
Lo primero que debemos hacer es importar nuestra función ValidatorHandler y los esquemas necesarios en nuestras rutas. Esto es esencial ya que las rutas son las que manejan el request y el response, y es aquí donde configuraremos qué esquema usar para cada endpoint.
Cada endpoint debe ejecutar y definir su propio esquema. El middleware se ejecutará en la ruta, acompañando a otros middlewares o procesos que cada endpoint tenga configurado. Esto asegura que la validación se realice justo antes de que el servicio se ejecute.
Por ejemplo, en un endpoint que recibe un ID como parámetro, debemos estar seguros que dicho ID cumpla con el formato esperado. Así lo aplicamos:
En algunos casos, una ruta necesita múltiples validaciones. Por ejemplo, un endpoint que requiere validar tanto parámetros del body como de la URL. Los middlewares se ejecutan de forma secuencial, por lo que podemos encadenarlos para verificar cada parte de los datos:
Cuando ocurre un error de validación, es importante brindar una respuesta clara y específica. Joy detecta errores y retorna un código HTTP 400 (Bad Request) si la validación falla. Inicialmente, Joy solo devuelve el primer error que encuentra. Sin embargo, podemos configurar Joy para que reporte todos los errores a la vez utilizando la opción abortEarly: false.
Con este ajuste, el usuario recibe todos los errores de validación de una vez, mejorando la experiencia del desarrollador y del usuario final.
¿Qué funciones básicas se deben cuidar en los esquemas?
Especificidad de campos: Define claramente campos obligatorios y sus tipos, como un string o número.
Restricciones de valores: Establece límites o reglas, como la longitud mínima y máxima, o tipo de dato específico (por ejemplo, alfanumérico).
Requerimiento de campos: Determina si un campo es obligatorio usando required.
URLs válidas: Define un campo como URL, si es necesario.
Este enfoque no solo optimiza la integridad de los datos al interactuar con nuestros servicios, sino que también mejora la estructura y mantenibilidad del código. Al integrar middlewares de validación en cada ruta, aseguramos que las solicitudes no válidas nunca lleguen a los servicios en producción, mejorando así la robustez de nuestro sistema.
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
constcb0=function(req, res, next){console.log('CB0')next()}constcb1=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!')})
Muchas gracias. :)
Grandioso!
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
Excelente aporte. Muy útil para un proyecto real considero yo
Excelente aporte!
Si alguno ve este error en la consola:
Error[ERR_HTTP_HEADERS_SENT]:Cannotset 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.
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:constJoi=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");constUserService=require("../services/userService");const validatorHandler =require("../middlewares/validatorHandler");const{createUserSchema, getUserSchema}=require("../schemas/userSchema");const router = express.Router();const service =newUserService();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.
Gracias por el aporte, no había probado ese caso de uso.
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.
Gracias por el aporte, tenía esa duda y me había confundido aún más cuando vi que mandó 'params' como un string, pero ya me ha quedado claro 👍
esto lo que hace es decir le de que forma recibe la data si es por params, body, query
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
¿Jaime, Cómo va con su emprendimiento?
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.
Alguien tiene idea de el por que de esto:
module.exports={ validationHandler };// No funcionamodule.exports= validationHandler ;// Funciona
Aquí se explica que es desestructurar.
El validatorHandler no es ni un objeto, ni un arreglo, por eso no se puede desestructurar, es una función.
Si funciona pero para importarlo tienes que usar destructuring también,
const { validatorHandler } = require('../middlewares/validator.handler');
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:
no es mi sintaxis favorita el joi pero muy util
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.
Hola a todos, descubrí un error pero no se como solucionarlo, ocurre al momento de hacer un post por ejemplo, si pasamos el precio como un string nos devuelve todos los errores menos el del precio, de alguna manera no se esta validando que sea un numero, ¿Alguien sabe por que?
Una disculpa nuevamente jajaja, encontré algo que es interesante lo que sucede es que yo enviaba el precio por ejemplo 500 entre comillas para que sea un str pero joi aun asi lo valida como numero y no arroja error a menos que tenga una letra.
Es que, el deber ser, es que envies el JSON en como un string, luego JS se encarga de "convertirlo" en un número. Además dentro de la especificaciones JSON, define que solo se debe hacer uso de strings.
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);
O usar un patron:
const name =Joi.string().pattern(/^[a-zA-Z0-9 ]+$/).min(3).max(15);// alphanum+numbers+space
Buenas, no me funcionan el boom, es decir si esta validando los errores, pero siempre me pasa al middleware de error y no el de boom entonces siempre me muestra error 500
Código del validator.handler.js
Código de error.hadler.js
Código de la función findOne()
Hola, a primera vista veo tu código bien, pero igual válida si en el index.js agregaste el middleware, debería estar así:
Me pasa exactamente lo mismo. ¿Pudiste solucionarlo?
Hola Platzinautas! Tengo un error y no encuentro como solucionarlo.
{"message":"Cannot read property 'validate' of undefined","stack":"TypeError: Cannot read property 'validate' of undefined\n at /home/maucoder/platzi_courses/my_store/middlewares/validatorHandler.js:6:30\n at Layer.handle [as handle_request] (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/layer.js:95:5)\n at next (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/route.js:137:13)\n at Route.dispatch (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/route.js:112:3)\n at Layer.handle [as handle_request] (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/layer.js:95:5)\n at /home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/index.js:281:22\n at param (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/index.js:354:14)\n at param (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/index.js:365:14)\n at Function.process_params (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/index.js:410:3)\n at next (/home/maucoder/platzi_courses/my_store/node_modules/express/lib/router/index.js:275:10)"}
Intente cambiar validate, pero no es eso y tampoco es la property que paso luego, así que no entiendo que pasa.
Hola,
El mensaje es medianamente claro, la variable validate no está definida, puede que la tengas declarada let validate pero no está inicializada o puede que la tengas declarada como Validate y luego la estás citando como validate o pueda que no exista en ningún lado del código.
Si subes tu proyecto a GitHub y compartes el link será mucho más fácil ayudarte.
Saludos
Hola.
Segun lo que me a pasado es que si esa es una propiedad de un objeto ejemplo: obj.validate, este error indicaria en el ejemplo anterior que la variable ob llega indefinida, normalmente me pasa cuando estoy recibiendo un objeto como parametro de una funcion y no valido el que el objeto que recibo este definido(osea que se envia algo) como doy por ejemplo abajo.