Ensuring Data Integrity: JSON Schema Validation in Node.js with Ajv
Tags: javascript, node, tutorial, webdev
Published: 2022-08-16 13:33:00

### Introduction

If you work with JavaScript and Node.js, most likely you've already felt the need to do data validation either on the frontend or on the backend.

There are several libraries and several approaches to perform data validation and most of the time we like to use abstractions, however sometimes we really need to define a JSON Schema and then validate the data through that same JSON Schema.

And for that same reason in today's article we are going to use Ajv, in addition to being a validator, it also has several plugins that help us to "extend" the validation criteria.

### Prerequisites

Before going further, you are expected to have basic knowledge of these technologies:

- Node
- NPM
- JSON Schema

## Getting Started

First let's create our project folder:

```sh
mkdir node-ajv
cd node-ajv
npm init -y
```

In the project's `package.json` add the following property:

```js
{
  "type": "module",
}
```

Now, let's install Ajv in our project:

```sh
npm install ajv --save
```

The next step will be to create our JSON Schema, which will be inside the `src/` folder in a file called `schema.js`:

```js
export const authSchema = {
  type: "object",
  properties: {
    username: {
      type: "string",
      description: "Username of the user",
    },
    email: {
      type: "string",
      description: "Email of the user",
    },
    password: {
      type: "string",
      description: "Password of the user",
      minLength: 8,
      maxLength: 24,
    },
  },
  required: ["username", "email", "password"],
  additionalProperties: false,
};
```

Then we can create the `validator.js`, this file will contain the Ajv instance as well as the factory that will be reused whenever we want to create a new validator:

```js
import Ajv from "ajv";
import { inspect } from "util";

const ajv = new Ajv({ allErrors: true });

export const validatorFactory = (schema) => {
  const validate = ajv.compile(schema);

  const verify = (data) => {
    const isValid = validate(data);
    if (isValid) {
      return data;
    }
    throw new Error(
      ajv.errorsText(
        validate.errors?.filter((err) => err.keyword !== "if"),
        { dataVar: "schemaValidation" } + "\n\n" + inspect(data)
      )
    );
  };

  return { schema, verify };
};
```

As you may have noticed, the `validatorFactory()` function takes in the arguments the `schema` (which corresponds to a JSON Schema) and returns two properties:

- `schema` - A "copy" of the schema that was passed in the function arguments (if you want you can do a deep copy)
- `verify` - receives in the arguments the data that we intend to validate and if this same data is valid the validation is successful and is returned, otherwise the error is thrown

For this article to be easily testable, we can create a simple api, first we install the dependencies:

```sh
npm install koa @koa/router koa-body --save
``` 

Then we create a base api:

```js
import Koa from "koa";
import Router from "@koa/router";
import koaBody from "koa-body";

const app = new Koa();
const router = new Router();

app.use(koaBody());

router.post("/", async (ctx) => {
  ctx.body = "Hello, World";
});

app.use(router.routes());

app.listen(3000);
```

Finally, we can import the schema that was created, as well as the validation factory, then we create a validator called `authValidation` and validate the data from the body of the http request. This way:

```js
import Koa from "koa";
import Router from "@koa/router";
import koaBody from "koa-body";

import { validatorFactory } from "./validator.js"; //  👈 added this
import { authSchema } from "./schema.js"; //  👈 added this

const authValidation = validatorFactory(authSchema); //  👈 added this

const app = new Koa();
const router = new Router();

app.use(koaBody());

// 👇 changes have been made here
router.post("/", async (ctx) => {
  const body = ctx.request.body;
  const data = authValidation.verify(body);
  ctx.body = { data };
});

app.use(router.routes());

app.listen(3000);
```

Now we can make an http request with the `POST` at `http://localhost:3000` like this:

```js
{
  "email": "random@mail.com",
  "username": "random",
  "password": "randomPaswword"
}
```

## Conclusion

As usual, I hope you enjoyed the article and that it helped you with an existing project or simply wanted to try it out.

If you found a mistake in the article, please let me know in the comments so I can correct it. Before finishing, if you want to access the source code of this article, I leave [here](https://github.com/FranciscoMendes10866/node-ajv-validation) the link to the github repository.