DEV Community

Cover image for Schema Validation with Yup and Express.js
Francisco Mendes
Francisco Mendes

Posted on

Schema Validation with Yup and Express.js

I feel lucky to live in an era where we have so many alternatives to do the same thing. A lot of people criticize this, but I think it's amazing to have libraries that do the same thing but with different approaches. I think this helps the programmer to implement a project with something that follows his reasoning.

But today this is not going to be the subject of the article. Today I am going to teach you how to make a validation system using Yup together with Express.js.

In the past I had written an article about how to do exactly the same thing we are going to do today, except using Joi, if you want to read the article click here.

If you've used Joi in the past you'll feel comfortable using Yup, both libraries are quite similar.

However I find Yup more intuitive, with a cleaner Api and at the same time it offers a great development experience.

And if you're one of those people who cares a lot about the size of your project's bundle, let me tell you that Yup is much lighter than Joi.

I hope I caught your attention, so now let's move on to the code.

Let's code

As always, let's install the necessary dependencies first.

npm i express yup --save
Enter fullscreen mode Exit fullscreen mode

Now we need to create a simple api in Express, similar to this:

const express = require("express");

const app = express();

app.use(express.json());

app.get("/", (req, res) => {
  return res.json({ message: "Validation with Yup 👊" });
});

const start = (port) => {
  try {
    app.listen(port, () => {
      console.log(`Api running at: http://localhost:${port}`);
    });
  } catch (err) {
    console.error(err);
    process.exit();
  }
};
start(3333);
Enter fullscreen mode Exit fullscreen mode

Now that we have the foundation of our project, we can start using Yup. First we will create a schema based on the following JSON (which will be the body of our http request):

{
  "title": "This is the title",
  "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
  "contact": "author@outlook.com",
  "url": "https://safe-link.me"
}
Enter fullscreen mode Exit fullscreen mode

Now let's create a new route in our api, in which we'll return the object data from the http request body and we'll still return the id from the params.

app.post("/create/:id", (req, res) => {
  return res.json({ body: req.body, id: req.params.id });
});
Enter fullscreen mode Exit fullscreen mode

Now with the route created, we only need two things, a middleware for validating the schema and the schema itself. So first we'll create our schema, not forgetting to import Yup into our project.

const yup = require("yup");

// Hidden for simplicity

const linkSchema = yup.object({
  body: yup.object({
    url: yup.string().url().required(),
    title: yup.string().min(8).max(32).required(),
    content: yup.string().min(8).max(255).required(),
    contact: yup.string().email().required(),
  }),
  params: yup.object({
    id: yup.number().required(),
  }),
});
Enter fullscreen mode Exit fullscreen mode

As we can see, we will validate the body of our http request and its parameters. However, if in your projects you also want to use query strings, you can also validate them.

This time I'm going to take a different approach, because Yup allows me to do that. This is because I will want to reuse the middleware several times and I just want it to validate the schema that I pass in the arguments.

So this way we will only write the middleware once and we will only have to create several individual schemas (just like we did with linkSchema).

Moving now to the creation of middleware so that everything I said earlier starts to make sense. We'll call the middleware validate.

const validate = (schema) => async (req, res, next) => {
  // logic goes here
};
Enter fullscreen mode Exit fullscreen mode

As you can see, in middleware we will receive the schema as a function argument, after that we will validate it, if everything is correct, we will have access to the controller.

const validate = (schema) => async (req, res, next) => {
  try {
    await schema.validate({
      body: req.body,
      query: req.query,
      params: req.params,
    });
    return next();
  } catch (err) {
    // More logic goes here
  }
};
Enter fullscreen mode Exit fullscreen mode

As you can see in the code, the middleware will be ready to validate the body, the params and the query strings, which makes it extremely flexible in this way.

Now, to finish the middleware, just return the respective error that occurred during the schema validation, as follows:

const validate = (schema) => async (req, res, next) => {
  try {
    await schema.validate({
      body: req.body,
      query: req.query,
      params: req.params,
    });
    return next();
  } catch (err) {
    return res.status(500).json({ type: err.name, message: err.message });
  }
};
Enter fullscreen mode Exit fullscreen mode

Now with the schema and the middleware created, just add it to the route, as follows:

app.post("/create/:id", validate(linkSchema), (req, res) => {
  return res.json({ body: req.body, id: req.params.id });
});
Enter fullscreen mode Exit fullscreen mode

Now, if you are going to send an http request to the endpoint, you can see that Yup will already do the respective validation of each of the fields, according to the rules stipulated by us in the schema.

And if everything is correct, we will see the data that we send in the http request in the response from the endpoint's response. However, if one of the fields is incorrectly filled, we will only see the error that occurred.

The final code is as follows:

const express = require("express");
const yup = require("yup");

const app = express();

app.use(express.json());

const linkSchema = yup.object({
  body: yup.object({
    url: yup.string().url().required(),
    title: yup.string().min(8).max(32).required(),
    content: yup.string().min(8).max(255).required(),
    contact: yup.string().email().required(),
  }),
  params: yup.object({
    id: yup.number().required(),
  }),
});

const validate = (schema) => async (req, res, next) => {
  try {
    await schema.validate({
      body: req.body,
      query: req.query,
      params: req.params,
    });
    return next();
  } catch (err) {
    return res.status(500).json({ type: err.name, message: err.message });
  }
};

app.get("/", (req, res) => {
  return res.json({ message: "Validation with Yup 👊" });
});

app.post("/create/:id", validate(linkSchema), (req, res) => {
  return res.json({ body: req.body, id: req.params.id });
});

const start = (port) => {
  try {
    app.listen(port, () => {
      console.log(`Api running at: http://localhost:${port}`);
    });
  } catch (err) {
    console.error(err);
    process.exit();
  }
};
start(3333);
Enter fullscreen mode Exit fullscreen mode

I hope I explained it in a simple way and that it helped you in your projects.

What about you?

What schema validation do you use in your projects?

Top comments (11)

Collapse
 
ramirezsandin profile image
Jorge

Hi Francisco nice article.

One thing I've been thinking is to use Yup to validate the URL query parameters as well before reading them, to check if they are correct and to ignore the invalids as well before sending them to the server.

What do you think that should be done when some parameters are not correct, ignore and remove them from the URL, or notify that something is not valid and show an interface with the error?

Collapse
 
noor_codes profile image
Noorullah Ahmadzai

Thank You so much. This help me a lot. Just wanted to you to know that :)

Collapse
 
mouslim2021 profile image
Mouslim2021

Thanks very much... It's very helpfull for me.

Collapse
 
jamesdev21 profile image
Semaj21

Hi. I am learning MERN stack. I have set up my express/mongodb as my backend. Now in my react client I am using react-form-hook with yup schema but how do I actually pass in the error response from express to validate on yup? I am validating for email uniqueness. Also, responses from express are not logging to console when I implemented the yup schema. I dont get the status 400, email is already used, anymore. Any help would be appreciated. thanks

Collapse
 
franciscomendes10866 profile image
Francisco Mendes

In the frontend you should only validate the input data, checking if they follow the rules specified by yourself and just that.

In the backend is another layer of "security" to verify that each of the data is correct. Now if you are validating if the email is unique this is not done by yup but by the ODM/ORM you use and the error has to be handled by you.

Something like this:

const exists = await Users.findOne({ email })
if (exists) {
     return res.json({message: "Email already taken."})
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jamesdev21 profile image
Semaj21 • Edited

Yes, I have two validations. From backend and frontend. backend is fine. It checks, validate and send a response. My backend sends an error response with "400, Email is already exists" if an email is already exists from mongodb. I tested this on Postman as well. Now my problem is how do I pass this response to react-form-hook with yup schema? that's where I'm stuck. The axios request is on react-form-hook onSubmit. Thanks btw

Thread Thread
 
franciscomendes10866 profile image
Francisco Mendes • Edited

Ah ok I get it, I would use .then() with axios. And then it would have two states with the useState() hook. Something like this.

axios.post('/').then((res) => {
   if (res.message) {
     setIsError(true)
     setErrorMessage(res.message)
   }
})
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
jamesdev21 profile image
Semaj21

Alright, thanks. I'll try it out.

Collapse
 
paras594 profile image
Paras 🧙‍♂️

Very well explained !

Collapse
 
franciscomendes10866 profile image
Francisco Mendes

Thanks for the feedback! 😊

Collapse
 
florianwaltherprivate profile image
Florian Walther

Thank you so much. This post clarified for me how to validate the whole request, including the params, and not only the body!