How to use celebrate with Node.js
Tags: node, javascript, webdev
Published: 2022-05-24 20:30:21

**Written by [Hulya Karakaya](https://blog.logrocket.com/author/hulyakarakaya/)✏️**

Imagine you have created a note-taking app with a login system where users can create an account and add their notes. Users need to type their email and name to sign in. Your job as a developer is to ensure the data you get from the user is the data you’re looking for, and it’s in the correct format, before persisting them in a database. 

Validating user input sent from user requests is highly important for a couple of reasons:

*   Helps mitigate the attack surface
*   Protects from attacks like [DDOS](https://en.wikipedia.org/wiki/Denial-of-service_attack), cross-site scripting, command injection and SQL injection
*   Ensures data consistency
*   Helps identify and filter malicious data

This type of validation is called server-side validation, and it’s a critical part of developing applications. Luckily, there are several libraries that take care of this task for us. 

Two of the best libraries for this are [joi](https://www.npmjs.com/package/joi) and [celebrate](https://www.npmjs.com/package/celebrate). Joi is an object schema description language and validator for JavaScript objects. In this article, we’ll look at how to use these libraries and the benefits they provide for frontend dev. 

By the end of this tutorial, you’ll be able to validate incoming user inputs coming from `req.body`, validate `req.headers`, `req.params`, `req.query`, and `req.cookies`, and handle errors. 

We'll demo some API routes for the note-taking app that requires user input and validates it. 

If you want to see the complete project developed throughout this article, take a look at the [GitHub project](https://github.com/hulyak/validation-with-celebrate). Feel free to clone it, fork it, or submit an issue.

## Contents

*   [What are joi and celebrate?](#what-are-joi-and-celebrate)
*   [Getting started with a sample Node.js app](#getting-started-with-a-sample-nodejs-app)
*   [Installing joi and celebrate for schema-based validation](#installing-joi-and-celebrate-for-schemabased-validation)
*   [Validating the request body with celebrate](#validating-the-request-body-with-celebrate)
*   [Error handling](#error-handling)
*   [Testing the endpoint](#testing-the-endpoint)
*   [Validating request query strings](#validating-request-query-strings)
*   [Validating request headers and parameters](#validating-request-headers-and-parameters)
*   [Validating cookies and signed cookies](#validating-cookies-and-signed-cookies)

## What are joi and celebrate?

Joi is a standalone validation module that can be used alongside celebrate. Joi describes the client request within a schema. A schema is a JavaScript object that describes how client requests like parameters, request body, and headers must be formatted. They are made of a type and a succession of rules, with or without parameters. 

Celebrate uses this schema to implement flexible validation middleware. It takes a schema and returns a function that takes the request and a value. If the value is valid, celebrate will call the next middleware in the chain. If the value is invalid, celebrate will call the error handler middleware. 

You can validate `req.params`, `req.headers`, `req.body`, `req.query`, `req.cookies` and `req.signedCookies` before any handler function is called. We'll go into detail about how to validate these later in this article.

## Getting started with a sample Node.js app

Begin by opening up your terminal and navigating to the directory where you want to place your project:

```shell
mkdir notes && cd notes
```

Create a new Node project by running:

```shell
npm init -y 
```

This will generate a `package.json` file in the root of your project. The `--yes` or `-y` flag will answer "yes" to all questions when setting up `package.json`. 

Now, install the required dependencies by running:

```shell
npm install express body-parser cookie-parser
npm install nodemon -D
```

Let’s review our installed packages:

*   [Express](https://expressjs.com/) is one of the most popular web framework for Node. It's used for creating web servers and APIs
*   [body-parser](https://github.com/expressjs/body-parser) is a middleware that parses the body of incoming requests, and exposes the resulting object on `req.body`
*   [cookie-parser](https://github.com/expressjs/cookie-parser) parses the cookies of incoming requests, and exposes the resulting object on `req.cookies`
*   [Nodemon](https://www.npmjs.com/package/nodemon) is used for automatically restarting the server when we make changes to our code.

The `npm init` command assigns `index.js` as the entry point of our application. Go ahead and create this file at the root of your project:

```shell
touch index.js
```

Next, open up your favorite code editor, and create the boilerplate code for instantiating Express and setting up the server:

```javascript
const express = require("express");
const bodyParser = require("body-parser");
const app = express();

// parse application/json
app.use(bodyParser.json());

const PORT = process.env.PORT || 4001;

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});
```

Here, we've imported Express and BodyParser and invoked the Express function to create our server. The server will listen on port 3000.

### Running the app

Go to your `package.json` file and add a script to run our server with `nodemon`:

```json
"scripts": {
    "start": "nodemon index.js"
  }
```

Now, we can run our server from terminal by running `npm start`. This will start nodemon and watch for changes in our code.

### Creating routes

Now that our application is listening for requests, we can create some routes:

*   POST `/signup` for creating a new user account
*   GET `/notes` for retrieving the notes
*   DELETE `/notes/:noteId` for deleting a note

Next, we'll look at how to validate the request data via joi and celebrate.

## Installing joi and celebrate for schema-based validation

We can install joi and celebrate via npm like so:

```shell
npm install joi celebrate
```

Joi allows you to describe data in an intuitive, readable way via a schema:

```javascript
{
  body: Joi.object().keys({
    name: Joi.string().alphanum().min(2).max(30).required(),
    email: Joi.string().required().email(),
    password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}/pre>)).required().min(8),
    repeat_password: Joi.ref('password'),
    age: Joi.number().integer().required().min(18),
    about: Joi.string().min(2).max(30),
  })
}
```

According to this schema, a valid `body` must be an object with the following keys:

*   `name`, a required string with at least two characters and up to 25 characters (alphanumeric characters only)
*   `email`, a required string in an email format
*   `password`, a required string with at least eight characters, which should match the custom regex pattern
*   `repeat_password`, which should match the password
*   `age`, a required number with an integer value of 18 or more
*   `about`, a string with at least two and up to 50 characters

Anything outside of these constraints will trigger an error.

## Validating the request body with celebrate

Now, we can use the celebrate library to enable joi validation as middleware. Import the package and connect it as a middleware to the route:

```javascript
const { celebrate, Joi, Segments } = require('celebrate');

app.post(
  "/signup",
  celebrate({
    [Segments.BODY]: Joi.object().keys({
      name: Joi.string().alphanum().min(2).max(30).required(),
      email: Joi.string().required().email(),
      password: Joi.string()
        .pattern(new RegExp("^[a-zA-Z0-9]{3,30}$"))
        .required()
        .min(8),
      repeat_password: Joi.ref("password"),
      age: Joi.number().integer().required().min(18),
      about: Joi.string().min(2).max(30),
    }),
  }),
  (req, res) => {
    // ...
    console.log(req.body);
    res.status(201).send(req.body);
  }
);
```

Here, we're using celebrate to validate the request body. 
Celebrate takes an object in which the key can be one of the values from `Segments` and the value is a joi schema. Segments is a set of named constants, `enum`, that can be used to identify the different parts of a request:

```json
{
  BODY: 'body',
  QUERY: 'query',
  HEADERS: 'headers',
  PARAMS: 'params',
  COOKIES: 'cookies',
  SIGNEDCOOKIES: 'signedCookies',
}
```

## Error handling

If we try out our endpoint for `signup` with a body that doesn't match the schema, we'll get the following error:

```json
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Error</title>
</head>
<body>
  <pre>Error: Validation failed<br> &nbsp; &nbsp;at /Users/hulyakarakaya/Desktop/celebrate/node_modules/celebrate/lib/celebrate.js:95:19<br> &nbsp; &nbsp;at processTicksAndRejections (node:internal/process/task_queues:96:5)</pre>
</body>
</html>
```

Celebrate has a special `errors()` middleware for sending errors to the client. By implementing this middleare, we can send more detailed error messages. Import `errors` from celebrate and pass it to the `app.use` method:

```javascript
const { errors } = require('celebrate');

// celebrate error handler
app.use(errors()); 
```

This middleware will only handle errors generated by celebrate. Let's see it in action!

## Testing the endpoint

We'll use Postman for testing our endpoint. Make sure your server is running before you test the endpoint. 

Make a POST request to the `/signup` route. If we don't correctly repeat the password, we should get an error. 

The error status returned by celebrate is `400`, and the response body is:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "Validation failed",
    "validation": {
        "body": {
            "source": "body",
            "keys": [
                "repeat_password"
            ],
            "message": "\"repeat_password\" must be [ref:password]"
        }
    }
}
```

Or, if we input an age that is lower than 18, we'll get a “Bad Request” error:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "Validation failed",
    "validation": {
        "body": {
            "source": "body",
            "keys": [
                "age"
            ],
            "message": "\"age\" must be greater than or equal to 18"
        }
    }
}
```

The `message` field allows the client to understand what is wrong with their request. In these cases, celebrate reports that the repeat password is not equal to the original password, and age must be greater than or equal to 18 in the request body.

## Validating request query strings

This will work similar to validating request body, but this time we will use `Segments.QUERY` as a key. 

Imagine we want to send user token in the query string when signing up:

```javascript
app.post(
  "/signup",
  celebrate({
    [Segments.BODY]: Joi.object().keys({
      // validation rules for the body
    }),
    [Segments.QUERY]: {
      token: Joi.string().token().required(),
    },
  }),
  (req, res) => {
    console.log(req.query.token);
    res.status(200).send(req.query.token);
  }
);
```

When we test the API endpoint, we need to add a `token` query string to the URL, and it shouldn't be empty. ![Validating Query String with celebrate](https://blog.logrocket.com/wp-content/uploads/2022/05/Validating-Query-String-with-celebrate.png) 

If we don't pass the `token` query string, celebrate will show an error message:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "Validation failed",
    "validation": {
        "query": {
            "source": "query",
            "keys": [
                "token"
            ],
            "message": "\"token\" is required"
        }
    }
}
```

## Validating request headers and parameters

In addition to the request body, celebrate allows you to validate headers and parameters:

```javascript
const { celebrate, Joi } = require('celebrate');

app.delete(
  "/notes/:noteId",
  celebrate({
    // validate parameters
    [Segments.PARAMS]: Joi.object().keys({
      noteId: Joi.string().alphanum().length(12),
    }),
    [Segments.HEADERS]: Joi.object()
      .keys({
        // validate headers
      })
      .unknown(true),
  }),
  (req, res) => {
    // ...
    res.status(204).send();
  }
);
```

In our example, we're creating a DELETE request to `/notes/:noteId`. `noteId` is a parameter, and it should be a 12-character alphanumeric string. 

To validate the headers, we can use the `Segments.HEADERS` key. However, it's hard to know all the headers that can be sent by the client. So, after calling the `keys()` method, we can use the `unknown(true)` option to allow unknown headers. 

If we try to DELETE a note ID that is less than 12 characters long (`http://localhost:3000/notes/123456`), we'll get the following error:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "Validation failed",
    "validation": {
        "params": {
            "source": "params",
            "keys": [
                "noteId"
            ],
            "message": "\"noteId\" length must be 12 characters long"
        }
    }
}
```

## Validating cookies and signed cookies

Celebrate also allows you to validate cookies and signed cookies. To read the cookies on the server, we'll use `cookie-parser`, the package we installed earlier. Let's connect it as a middleware in the `index.js` file:

```javascript
const cookieParser = require("cookie-parser");

const app = express();

app.use(cookieParser("secret"));
```

Cookies can be stored in local data files. We can set cookies using the `res.cookie()` method:

```javascript
res.cookie("name", "john", { httpOnly: true, maxAge: 3600000});
```

The first argument is the key, and the second, the value. The third argument is an object that contains the options for the cookie. `httpOnly: true` means that the cookie can't be read from JavaScript and `maxAge` is the time in milliseconds that the cookie will be valid. So, the cookie will expire after one hour. 

Cookie-parser will help us extract the data from the Cookie header and parse the result into an object. We can now access the cookies on the server using the `req.cookies` object. 

Now, we can add our validation to the `Segments.COOKIES` key:

```javascript
app.get(
  "/notes",
  celebrate({
    // validate parameters
    [Segments.COOKIES]: Joi.object().keys({
      name: Joi.string().alphanum().min(2).max(30),
    }),
  }),
  function (req, res) {
    res.cookie("name", "john", { httpOnly: true, maxAge: 3600000 });
    console.log("Cookies: ", req.cookies);
    res.send(req.cookies.name);
  }
);
```

Signed cookies are similar to cookies, but they contain a signature so that the server can verify whether or not the cookie is modified:

```javascript
app.get(
  "/notes",
  celebrate({
    [Segments.SIGNEDCOOKIES]: Joi.object().keys({
      jwt: Joi.string().alphanum().length(20),
    }),
  }),
  function (req, res) {
    // signed cookie
    res.cookie("jwt", "snfsdfliuhewerewr4i4", { signed: true });
    console.log("Signed Cookies: ", req.signedCookies);
    res.send(req.signedCookies);
  }
);
```

Here, we've set `jwt` to be a signed cookie by passing the `signed: true` option and created a validation rule with `Segments.SIGNEDCOOKIES`. Now, we can access the signed cookie on the server using the `req.signedCookies` object. If we try to send a `jwt` cookie that is less than 20 characters long, we'll get the following error:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "Validation failed",
    "validation": {
        "signedCookies": {
            "source": "signedCookies",
            "keys": [
                "jwt"
            ],
            "message": "\"jwt\" length must be 20 characters long"
        }
    }
}
```

## Conclusion

In this post, we've learned why you need to validate user inputs, and how to use joi and celebrate to validate user inputs, headers, query strings, parameters, cookies, and signed cookies. Also, we learned celebrate’s error handling abilities and how to test our endpoints using Postman. I hope you find this tutorial helpful, feel free to let us know in the comments if there is anything that is unclear.

---
##200’s only :heavy_check_mark: Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket.](https://logrocket.com/signup/)

[![LogRocket Sign Up](https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.