Search code examples
typescriptexpressvalidationmernyup

Express Yup to display all errors of all fields


I am new to express but not in programming. I have created a validation middleware that uses yup to validate the body being sent to the server

userRoutes.ts

import validate from "@middlewares/validationMiddleware.ts";
import userSchema from "@schemas/models/userSchema.ts";

router.post("/signup", validate(userSchema), signupUser); #signupUser is the controller/service for logic

userSchema.ts

import * as Yup from "yup";

const userSchema = Yup.object({
  body: Yup.object().shape({
    password: Yup.string()
      .min(8, "Password must be at least 8 characters")
      .matches()... // there are 4 matches here. too much code if I add all
      .required(),
    confirmPassword: Yup.string()
      .min(8, "Password must be at least 8 characters")
      .matches()... // there are 4 matches here. too much code if I add all
      .oneOf([Yup.ref("password"), undefined], "Passwords must match")
      .required(),
  }),
});

export default userSchema;

validationMiddleware.ts

import { NextFunction, Request, Response } from "express";
import { AnySchema, ValidationError } from "yup";

const validate =
  (schema: AnySchema) =>
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      await schema.validate(
        {
          body: req.body,
          query: req.query,
          param: req.params,
        },
        {
          abortEarly: false, // get all errors on all fields
        }
      );

      next();
    } catch (e) {
      if (e instanceof ValidationError) {
        console.log(e.errors); // shows all errors as an array (no key)
        const validationErrors: any = {};
        e.inner.forEach((error) => {
          if (error.path) {
            validationErrors[error.path] = error.message;
          }
        });
        console.log(validationErrors); // displays errors as an object but value is only 1 of the errors
        return res.status(400).json(validationErrors);
      }
      return res.status(500).send("Internal Server Error");
    }
  };

export default validate;

output

  // e.errors
  [
   'Password must have 1 lowercase character',
   'Password must have 1 digit character',
   'Password must have 1 uppercase character',
   'Password must be at least 8 characters',
   'body.password is a required field',
   'Password must have 1 lowercase character',
   'Password must have 1 digit character',
   'Password must have 1 uppercase character',
   'Password must be at least 8 characters',
   'body.confirmPassword is a required field'
 ]

 // validationErrors
 {
   'body.password': 'body.password is a required field',
   'body.confirmPassword': 'body.confirmPassword is a required field'
 }

As you can see on my code and output, the e.errors has all the validation errors that I want to be returned but it is being returned as an array so I dont know which field I should add them while in validationErrors it is returned as an object but there is only 1 error per field.

How should I proceed?


Solution

  • I think I found my answer.

    in

    if (error.path) {
       validationErrors[error.path] = error.message;
    }
    

    error.path has multiple error.message so it should be

    if (error.path) {
      if (!validationErrors[error.path]) {
        validationErrors[error.path] = []; // Initialize array message
      }
      validationErrors[error.path].push(error.message); // push message to array
    }