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

Extract data using web scraping with Python
admin14/03/2024

Extract data using web scraping with Python
A beginner’s guide to learn web scraping with Python!
Next.js 14 App Router Localization with next-intl
admin07/07/2024

Next.js 14 App Router Localization with next-intl
In this article, I will illustrate how to implement next-intl localization in a Next.js 14 application
TypeScript Design Pattern - Proxy
admin11/08/2023

TypeScript Design Pattern - Proxy
Provide a surrogate or placeholder for another object to control access to it.
Mới nhất

Create Cognito User Pool with AWS CDK
admin09/06/2023

Create Cognito User Pool with AWS CDK
In the previous post, I showed you how to create a simple S3 bucket. Next, in this article, I will guide you to create a Cognito User Pool.
Microservice in a Monorepo
admin22/06/2023

Microservice in a Monorepo
Microservice in a Monorepo
TypeScript Design Pattern - Bridge
admin08/08/2023

TypeScript Design Pattern - Bridge
Decouple an abstraction from its implementation so that the two can vary independently.
Đ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