DEV Community

Bernhard Häussermann
Bernhard Häussermann

Posted on • Edited on

2 1

Adding request and response validation to your Express REST API

This article is a continuation of a series about how to build a REST API in Node.js. In the first article of this series, we created a rudimentary REST API for managing a list of employees. But that API doesn't perform any validation on the requests that are received. Therefore, nothing stops the consumer from making a request with incorrect query parameters or a malformed body. So we could for example send the following request:

POST localhost:3000/employees
{
  "surname": "Symonds",
  "firstname": "Andrew"
}
Enter fullscreen mode Exit fullscreen mode

The API would happily add the specified object to the list of employees, even though it has the wrong names for the properties ("surname" instead of "lastName" and "firstname" instead of "firstName").

The express-openapi-validator package addresses this issue by validating requests and responses based on a provided OpenAPI spec. Having our responses validated and not just the requests will be a good idea as that will ensure the API code always responds in the expected schema.

express-openapi-validator provides a function that takes an OpenAPI specification as a parameter and returns a request handler that we can add to Express. The most straightforward way to use it would be to write our OpenAPI spec in a JSON or YAML file and read it from this file to pass into express-openapi-validator. But this would mean that whenever we make changes to the API in future, the corresponding part of the OpenAPI spec would have to be updated in this separate file. It would be much easier to keep the OpenAPI spec up to date if it were defined along with the relevant code.

The swagger-jsdoc package enables us to do this. It looks for JSDoc comments in code (annotated with an @openapi tag) to generate the OpenAPI specification.

Add the express-openapi-validator and swagger-jsdoc packages as run-time dependencies:

npm install --save express-openapi-validator swagger-jsdoc
Enter fullscreen mode Exit fullscreen mode

Let's add the JSDoc comments for defining the POST /employees operation. This involves an employee structure, which we can define as a schema object on the EmployeesController class:

/**
* @openapi
* components:
* schemas:
* EmployeeForAdd:
* type: object
* properties:
* lastName:
* type: string
* firstName:
* type: string
* title:
* type: string
* required: [ lastName, firstName, title ]
*/
export class EmployeesController {
...

We can then reference this schema object in a comment that we add just before the POST /employees operation:

export class EmployeesController {
static registerRoutes(app, service) {
...
/**
* @openapi
* /employees:
* post:
* summary: Add a new employee
* operationId: AddEmployee
* tags: [ Employees ]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/EmployeeForAdd"
* responses:
* "200":
* description: successful operation
* content:
* application/json:
* schema:
* type: object
* properties:
* id:
* type: integer
* format: int32
*/
app.post('/employees', (req, res) => {
...
});
...
}
}

We use swaggerJsDoc to generate the OpenAPI specification by telling which source files to look for the JSDoc comments via the apis property. We then register the generated OpenAPI spec by passing it into openApiValidator. We add all of this just before the registerRoutes() call:

...
import swaggerJsDoc from 'swagger-jsdoc';
import openApiValidator from 'express-openapi-validator';
...
const swaggerJsDocOptions = {
definition: {
openapi: '3.0.0',
info: {
title: 'Employees API',
version: '1.0.0',
description: 'A REST service for managing an employees data store.'
}
},
apis: ['./src/controllers/*.js']
};
const apiSpec = swaggerJsDoc(swaggerJsDocOptions);
app.use(openApiValidator.middleware({
apiSpec,
validateRequests: true,
validateResponses: true
}));
view raw server.js hosted with ❤ by GitHub

We can also set a route to serve the OpenAPI spec from localhost:3000/swagger.json as an easy way to access the generated OpenAPI spec. This can help with troubleshooting by ensuring the spec is generated as expected and allows our API consumers to access the spec easily.

The following statement needs to be added before the app.use() call which registers the validation. If it's added after the app.use() a request to localhost:3000/swagger.json will be validated and consequently return a 404 response since the /swagger.json route is not defined in our app's OpenAPI spec.

app.get('/swagger.json', (_req, res) => res.json(apiSpec));
view raw server.js hosted with ❤ by GitHub

Now, if we make the POST request as above, we get the following 400-code response:

{
"message": "request.body should have required property 'lastName', request.body should have required property 'firstName', request.body should have required property 'title'",
"errors": [
{
"path": ".body.lastName",
"message": "should have required property 'lastName'",
"errorCode": "required.openapi.validation"
},
{
"path": ".body.firstName",
"message": "should have required property 'firstName'",
"errorCode": "required.openapi.validation"
},
{
"path": ".body.title",
"message": "should have required property 'title'",
"errorCode": "required.openapi.validation"
}
]
}

The following post will show how we can leverage the generated OpenAPI spec to add an interactive Swagger documentation page to our API.

The code for the API developed in this series of articles is available on GitHub here.

Neon image

Serverless Postgres in 300ms (!)

10 free databases with autoscaling, scale-to-zero, and read replicas. Start building without infrastructure headaches. No credit card needed.

Try for Free →

Top comments (0)

👋 Kindness is contagious

Dive into this insightful write-up, celebrated within the collaborative DEV Community. Developers at any stage are invited to contribute and elevate our shared skills.

A simple "thank you" can boost someone’s spirits—leave your kudos in the comments!

On DEV, exchanging ideas fuels progress and deepens our connections. If this post helped you, a brief note of thanks goes a long way.

Okay