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

Part 1: React Props and State
admin18/06/2023

Part 1: React Props and State
Before learning React, we should understand Props and State. This is basic for a newbie.
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 4: How to use Redux Toolkit in React
admin18/06/2023

Part 4: How to use Redux Toolkit in React
In this article, I will explain Redux and delve into Redux Toolkit. a collection of tools that simplify using Redux. These tools help make Redux less daunting and easier to use.
Mới nhấ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.
TypeScript Design Pattern - Proxy
admin11/08/2023

TypeScript Design Pattern - Proxy
Provide a surrogate or placeholder for another object to control access to it.
Part 3: Upgrade Latest Ghost Ver On AWS Lightsail
admin17/06/2023

Part 3: Upgrade Latest Ghost Ver On AWS Lightsail
You are a beginner with Ghost CMS, Bitanami, AWS Lightsail and don't know much about the documentation yet. So, in this article, I introduce step by step to upgrade Ghost CMS to the latest version.
Đ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