REST APIs are the core while building cloud native applications in node.js . The most important aspect of being able to serve HTTP requests using REST APIs is to be able to validate the input payload and handle incorrect input with a proper error message. In the previous post I have explained how we can validate input payload using Joi. In this post I will show how we can achieve the same using the express-validation module.
Contents
d. The Model
e. The Router
What is Joi?
Joi is a data validator for JavaScript. Using Joi we can validate data in any JavaScript based frontend or node.js based backend application.
How to use Joi in node.js application to validate input from REST API?
This is the whole purpose of this post today. I will show with a code example how we can use Joi to validate the data that comes as input to the REST endpoints in an node.js application server.
The project structure
input_validators
|-- UserValidator.js
model
|-- User.js
routes
|-- user.js
errorHandeler.js
responseHandler.js
node-app-server.js
package.json
The package.json
In the package.json we have to include the express-validation as shown and install it using the npm install command
{
"name": "Test-App",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1",
"express-validation": "^3.0.6"
},
"devDependencies": {
"pm2": "^4.5.0"
},
"engines": {
"node": "^12.18.0",
"npm": "^6.14.4"
}
}
The Application Server
const express = require('express');
const app = express();
const port = 5000;
const bodyParser = require('body-parser');
const userRouter = require('./routes/user.js')();
const processError = require('./errorHandler')();
app.use(bodyParser.json());
app.use(userRouter);
app.use(processError);
app.listen(port, () => {
console.log(`My app listening at http://localhost:${port}`);
});
The Model
function User() {
return Object.freeze({
fetch,
create
});
async function create(request) {
return 'Hello World From User Post ' + request.params.id;
}
}
module.exports = User;
The Router
We will apply the validation in the router just before the model is called to process the request and the response is generated. We will add a few lines to our user.js router that handles the user creation POST requests. For each entity like the user there will be a different set of input fields. We are using the UserValidator that contains the schema to validate input for user creation. The validation method provided by the express-validation does the validation for us. We just need to provide the validation schema against which the validation will be done.
routes/user.js
const router = require('express').Router();
const processResponse = require('../responseHandler')();
const User = require('../model/User')();
const UserValidator = require('../input_validators/UserValidator');
const {validate} = require('express-validation');
module.exports = function () {
router.post('/user/:id',
validate(UserValidator.createValidationObject(), {}, {}),
processResponse(User.create)
);
return router;
}
The User Validator
As mentioned above the user validator should contain all the validation schemas needed for different APIs serving User related requests. The constant validationObject defines the validation schema needed during user creation and does the validation. Here we use Joi schema. Note that we are defining the params element for URL parameters in the request object and the body element for the request payload. As you have already noticed by now that express-validation is also using the Joi to for the validation schema . That is why we define the validation schema using validation types and constraints for each field using the Joi.object(). The types and restrictions can be applied using method channing. For explanation and details see the API documentation of express-validation. In the router we are using this validationObject to validate the request input.
input_validators/UserValidator.js
const {Joi} = require('express-validation');
const validRoles = [ "Admin", "Editor", "Reader", "None"];
const validationObject = {
params: Joi.object({id: Joi.number().integer().required()}),
body: Joi.object({
groupid: Joi.number().integer().positive().required(),
role: Joi.string().trim().valid(...validRoles).default("None"),
name: Joi.string().trim().required(),
privileged: Joi.boolean().default(false)
}),};
module.exports = {
createValidationObject: function() {
return validationObject;
}
};
The Error Handler
When the express-validator invalidates the input it will throw a ValidationError object. We have enhanced our error handler to react on that object. The ValidationError has a structure so we just extract the error message and formulate the HTTP error response.
errorHandler.js
const {ValidationError} = require('express-validation');
function errorHandler() {
return (err, req, res, next) => {
if (err instanceof TypeError) {
return res.status(400).json(err.name + ": " + err.message);
}
if(err instanceof ValidationError) {
let message = err.message;
if(err.details.body) {
message += err.details.body[0].message;
} else if(err.details.params) {
message += err.details.params[0].message;
}
return res.status(400).json(message);
}
if (err && err.statusCode) {
return res.status(err.statusCode).json(err.body);
}
return next(err);
}
}
module.exports = errorHandler;
The Response Handler
See the response handler in the blog post Separate routing from business logic in node.js || Central response generation in node.js
Verification of the above code
First we need to install all packages using npm install and start the application server using node node-app-server.js
Let’s generate a POST request to create an user http://localhost:5000/user/abcd
with body
{
"groupid": 123,
"role": "Admin",
"name": "Dibyo"
}
It returns
Hello World From User Post abcd
with 200 HTTP status code.
Let's change the user ID and body. POST request to create an user http://localhost:5000/user/1234
{
"groupid": 123,
"role": "Admin1",
"name": "Dibyo"
}
Now we get this as a response with HTTP status code 400.
"Validation Failed\"role\" must be one of [Admin, Editor, Reader, None]"
So we have corrected the user id field in the URL but had a wrong role. Let’s correct the payload body
{
"groupid": 123,
"role": "Admin",
"name": "Dibyo"
}
Now the endpoint will return
Hello World From User Post 1234
This is a good example of validating inputs using express-validation in a node.js application.
Comments