Danh mụcThẻBài viết

admin

I'm a Full-stack developer

Thẻ

Linked List
Data Structure
Chat GPT
Design Pattern
Microservices
API
AWS CDK
ReactJS
AWS Lightsail
Flutter Mobile
JOI - API schema validation
Ngày đăng: 12/06/2023

In this article, I introduce about Joi validation module to check data.


Data validation is one of topics that I am interesting. I always review my code after developed features or fixed bugs. There are many places where need to validate data, it is really terrible. Some cases, we need to validate data input because ensure the data into API, it will not make any problems to crash system.


As in the example below, the logic to check the data is really complex.


if (!req.body.email) {
  throw new Exception('Email is missing.') 
}

if (!req.body.password) {
  throw new Exception('Password is missing.') 
}

if (req.body.password.length < 8) {
  throw new Exception('Password must be at least 8 characters.') 
}


In this article, I introduce about Joi validation module to check data.


What is JOI?


Joi is a library that can help you check if the JSON structure is correct with the structure you want.


Why use Joi to check data before processing?


Verify quickly.

Limit exceptions.

Limit unnecessary processing when the user inputs the wrong value.

Ensure data integrity, and limit data rollback in the database.


JOI validation into REST API using Express.

Create project
mkdir joi-schema-validation


Go to the folder you just created above
cd joi-schema-validation


Install dependencies
npm install body-parser express joi


Create a new file app.ts in the root directory to set up the Express app
import express, { Express, NextFunction } from 'express';

import { json, urlencoded } from 'body-parser';

const app: Express = express();

app.use(urlencoded({ limit: '10mb', extended: true, parameterLimit: 50000 }));

app.use('/api', routes);

app.listen(9000, () => {
  console.info('Listening to port 9000');
});


Create a new file routes.ts
const router = express.Router();
const defaultRoute: IRoute[] = [
  {
    path: '/auth',
    route: new AuthRoute().getRouter(),
  },
];

defaultRoute.forEach((route) => {
  router.use(route.path, route.route);
});

export default router;


Create a directory containing schemas
mkdir schema-validation


Create the file base.schema-validation.ts to contain the base validations
import Joi from 'joi';

export class BaseSchemaValidation {
  protected readonly _httpRequest: { params?: any; query?: any; body?: any };

  constructor() {
    this._httpRequest = {};
  }
  
  withBody(body) {
    this._httpRequest.body = body;

    return this;
  }
  
  build() {
    return {
      ...(this._httpRequest.body && { body: this._httpRequest.body }),
      ...(this._httpRequest.query && { query: this._httpRequest.query }),
      ...(this._httpRequest.params && { query: this._httpRequest.params }),
    };
  }
}


Create the file login.schema-validation.ts to check the input for the login route
import Joi from 'joi';

export class LoginSchemaValidation extends BaseSchemaValidation {
  constructor() {
    super();
  }
  
  login() {
    return super
      .withBody({
        email: Joi.string().email().required(),
        password: Joi.string().custom(password).required(),
      })
      .build();
  }
}

const password = (value: string, helpers: CustomHelpers) => {
  if (value.length < 8) {
    return helpers.message({ custom: 'password must be at least 8 characters' });
  }
  if (!value.match(/\d/) ?? !value.match(/[a-zA-Z]/)) {
    return helpers.message({ custom: 'password must contain at least 1 letter and 1 number' });
  }
  return value;
};


Create a new folder middlewares in the root directory
mkdir middlewares


Then create a new file schema-validator.middleware.ts
import { NextFunction, Request, Response } from 'express';

import Joi, { ValidationOptions } from 'joi';

const pick = (object: Record<string, any>, keys: string[]) =>
  keys.reduce((obj: any, key: string) => {
    if (object && Object.prototype.hasOwnProperty.call(object, key)) {
      obj[key] = object[key];
    }
    return obj;
  }, {});

const schemaValidatorMiddleware = (schema: any, body?: any, options?: ValidationOptions) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      const validSchema = pick(schema, ['params', 'query', 'body']);
      const object = pick(req, Object.keys(validSchema));
      const { value, error } = Joi.compile(validSchema)
        .prefs({ errors: { label: 'key' } })
        .validate(object, { abortEarly: false });

      if (error) {
        console.log(
          `Validate request has an error ${JSON.stringify({
            ...error,
          })}`,
        );

        const errorMessage = {};

        error.details.forEach((e: any) => {
          errorMessage[e.path[0]] = [...(errorMessage[e.path[0]] ?? []), e.message];
        });

        return res.status(422).json(errorMessage);
      }

      Object.assign(req, value);

      return next();
    } catch (error: any) {
      console.log('Validate request has an error', {
        ...error,
      });

      return res.status(422).json({
        message: 'Validate request has an error'
      })
    }
  };
};

export { schemaValidatorMiddleware };


Add the schema validator middleware to the auth route auth.route.ts
export class AuthRoute {
  initialRoutes() {
    this.loginWithPassword([schemaValidatorMiddleware(new LoginSchemaValidation().login())]);
  }
  
  login(middleware: any = []) {
    this.router.post('/login', middleware, (req: Request, res: Response, next: NextFunction) =>
      this.controller.login(req, res, next),
    );
  }
}


Testing


Let's run your application
npm start


No body request



Email and password are not in the correct format


Summary


In this article, you created a schema to check input data before executing logic for a REST API using Joi and validating data from an HTTP request using middleware.

Having consistent data ensures that it will behave in a reliable and expected way when you reference it in your application.


Good luck to you, hope this post is of value to you!!!!

Đề xuất

Design Patterns
admin07/08/2023

Design Patterns
The design pattern does not be a specific programming language. Almost programming languages might apply design patterns that to resolve a problem repeat.
How to integrate ChatGPT-3.5 Turbo into Node.js
admin10/01/2024

How to integrate ChatGPT-3.5 Turbo into Node.js
Step-by-Step Guide to Incorporating ChatGPT-3.5 Turbo into Node.js for Basic ReactJS Applications
Form validator in Flutter with EzValidator
admin04/01/2024

Form validator in Flutter with EzValidator
When I am working on Flutter with form. For ensuring data integrity, and handling user input errors. I want to show an error message below each TextField, Dropdown, Switch, ... if the user does not input or wrong input. The EzValidator help me to resolve this.
Mới nhất

Part 1: Build a Chat App with ReactJS + Material UI
admin13/09/2023

Part 1: Build a Chat App with ReactJS + Material UI
In this article, I would like to introduce using ReactJS and material UI to build a Chat App.
How to secure your API gateway
admin17/04/2024

How to secure your API gateway
In this blog, I will cover the 6 methods that technology leaders need to incorporate to secure and protect APIs.
Part 2: Setup Custom Domain Zone + SSL for Ghost on AWS Lightsail
admin17/06/2023

Part 2: Setup Custom Domain Zone + SSL for Ghost on AWS Lightsail
In this section, I will continue to show you how to point Ghost Instance Static IP to your domain.
Đinh Thành Công Blog

My website, where I write blogs on a variety of topics and where I have some experiments with new technologies.

hotlinelinkedinskypezalofacebook
DMCA.com Protection Status
Góp ý
Họ & Tên
Số điện thoại
Email
Nội dung
Tải ứng dụng
hotline

copyright © 2023 - AGAPIFA

Privacy
Term
About