24-Nodejs Course 2023: Validation Part II: Rules List
Tags: node, typescript, mongodb, fastify
Published: 2022-11-02 05:52:11

---
title: 24-Nodejs Course 2023: Validation Part II: Rules List
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
#cover_image: 
# Use a ratio of 100:42 for best results.
---

> If you're still reading this series, you're awesome! I'm glad you're enjoying it. If you're just joining us, you can find us here in [discord](https://discord.gg/pb2vmdfhGf). I'm always happy to answer questions and help you out.

We have started our validation in our previous article, now let's get a head directly into the Rule class

## Rule Class

The Rule class will receive:

- It receives the input name.
- It receives the input value.
- It receives the input rules.

Now the rule will start validating the input value based on the rules.

Another thing that i just remembered, rule can also need another value from the request body, so we need to pass the request body to the rule class.

```ts
// src/core/validator/rule.ts

import { Request } from "core/http/request";

export default class Rule {
  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Request instance
   */
  protected request!: Request;

  /**
   * Constructor
   */
  public constructor(
    protected readonly input: string,
    protected readonly value: any,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Set request
   */
  public setRequest(request: Request) {
    this.request = request;

    return this;
  }
}
```

Now let's update the validator class to inject the request to the rule class.

```ts
// src/core/validator/index.ts
  /**
   * Scan the validation rules
   */
  public async scan() {
    // get inputs values
    const inputsValues = this.request.only(Object.keys(this.rules));

    for (const input in this.rules) {
      const inputValue = inputsValues[input];
      const inputRules = this.rules[input];

      const rule = new Rule(input, inputValue, inputRules);

      rule.setRequest(this.request);

      await rule.validate();

      if (rule.fails()) {
        this.errorsList.push(rule.errors());
      }
    }
  }
```

Now let's start working on the rule class.

## Rules List

Oops, i forgot something, that `Rule` class is supposed to validate a list of rules, it is not supposed to be the rule itself, so let's rename it to `RulesList`.

Now we'll start create a `rules` directory inside the validator, each rule will be a class inside this directory.

So now our `Validator` class will look like this:

```ts
// src/core/validator/index.ts
import { Request } from "core/http/request";
import RulesList from "./rules-list";

export default class Validator {
  /**
   * Errors list
   */
  protected errorsList: any[] = [];

  /**
   * Constructor
   */
  public constructor(
    protected readonly request: Request,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Scan the validation rules
   */
  public async scan() {
    // get inputs values
    const inputsValues = this.request.only(Object.keys(this.rules));

    for (const input in this.rules) {
      const inputValue = inputsValues[input];
      const inputRules = this.rules[input];

      const rule = new RulesList(input, inputValue, inputRules);

      rule.setRequest(this.request);

      await rule.validate();

      if (rule.fails()) {
        this.errorsList.push(rule.errors());
      }
    }
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
```

And our rules list will look like this:

```ts
// src/core/validator/rules-list.ts
import { Request } from "core/http/request";

export default class RulesList {
  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Request instance
   */
  protected request!: Request;

  /**
   * Constructor
   */
  public constructor(
    protected readonly input: string,
    protected readonly value: any,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Set request
   */
  public setRequest(request: Request) {
    this.request = request;

    return this;
  }
}
```

## Rules List Workflow

Now what we'll do is to create a `validate` method in our rules list.

This method will loop through the rules and call the rule class for each rule.

After that the rule class will receive the input **name and value**.

Then the rule will have the same exact validation method as the rules list and validator:

- `validate`
- `fails`
- `passes`
- `errors`

Now let's implement these first in the rules list.

```ts
// src/core/validator/rules-list.ts
import { Request } from "core/http/request";

export default class RulesList {
  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Request instance
   */
  protected request!: Request;

  /**
   * Constructor
   */
  public constructor(
    protected readonly input: string,
    protected readonly value: any,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Set request
   */
  public setRequest(request: Request) {
    this.request = request;

    return this;
  }

  /**
   * Start Validating
   */
  public async validate() {
    //
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
```

## Start Rules List validation

Now let's implement our validation as we said before.

```ts
// src/core/validator/rules-list.ts

  /**
   * Start Validating
   */
  public async validate() {
    //
    for (const ruleName of this.rules) {
      // get the rule class handler
      const Rule = (RulesList.rulesList as any)[ruleName];

      // create a new object from that rule class
      // pass to it the input name and the input value
      const rule = new Rule(this.input, this.value);

      // inject the request class
      rule.setRequest(this.request);

      // validate

      await rule.validate();

      // if fails
      // push the rule to errors list
      if (rule.fails()) {
        this.errorsList.push(rule.errors());
      }
    }
  }
```

We added so many things here, let's explain them.

First we loop through the rules list, then we get the rule class handler from the `rulesTypes` which is a static property that contains an object of rules, the key will be the rule name `i.e required` and its value will be the Rule class handler `i.e RequiredRule`, then we create a new object from that rule class and pass to it the input name and the input value.

Then we injected the request class to the rule class, then we validate the rule, and if it fails we push the rule to the errors list.

Now let's implement the `rulesTypes` static property.

```ts
// src/core/validator/rules-list.ts
import { Request } from "core/http/request";

export default class RulesList {
  /**
   * Rules List
   */
  public static rulesList = {
    required: RequiredRule,
  };

  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Request instance
   */
  protected request!: Request;

  /**
   * Constructor
   */
  public constructor(
    protected readonly input: string,
    protected readonly value: any,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Set request
   */
  public setRequest(request: Request) {
    this.request = request;

    return this;
  }

  /**
   * Start Validating
   */
  public async validate() {
    //
    for (const ruleName of this.rules) {
      // get the rule class handler
      const Rule = (RulesList.rulesList as any)[ruleName];

      // create a new object from that rule class
      // pass to it the input name and the input value
      const rule = new Rule(this.input, this.value);

      // inject the request class
      rule.setRequest(this.request);

      // validate

      await rule.validate();

      // if fails
      // push the rule to errors list
      if (rule.fails()) {
        this.errorsList.push(rule.errors());
      }
    }
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
```

## Too Many Request Injections

If we see, the `request` class is being injected in almost everywhere, the validator class, the rules list class, and the rule classes.

But we can actually access it directly as it is already a singleton request, so if we want it we can simply import it directly.

Now let's clean up our mess:

Request Class:

```ts
// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    if (this.handler.validation) {
      const validator = new Validator(this.handler.validation.rules);

      await validator.scan(); // start scanning the rules

      if (validator.fails()) {
        return this.response.status(422).send({
          errors: validator.errors(),
        });
      }
    }

    return await this.handler(this, this.response);
  }
```

Validator Class:

```ts
// src/core/validator/index.ts
import request from "core/http/request";
import RulesList from "./rules-list";

export default class Validator {
  /**
   * Errors list
   */
  protected errorsList: any[] = [];

  /**
   * Constructor
   */
  public constructor(protected readonly rules: any) {
    //
  }

  /**
   * Scan the validation rules
   */
  public async scan() {
    // get inputs values
    const inputsValues = request.only(Object.keys(this.rules));

    for (const input in this.rules) {
      const inputValue = inputsValues[input];
      const inputRules = this.rules[input];

      const rule = new RulesList(input, inputValue, inputRules);

      await rule.validate();

      if (rule.fails()) {
        this.errorsList.push(rule.errors());
      }
    }
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
```

Rules List Class:

```ts
// src/core/validator/rules-list.ts
export default class RulesList {
  /**
   * Rules List
   */
  public static rulesList = {
    required: RequiredRule,
  };

  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Constructor
   */
  public constructor(
    protected readonly input: string,
    protected readonly value: any,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Start Validating
   */
  public async validate() {
    //
    for (const ruleName of this.rules) {
      // get the rule class handler
      const Rule = (RulesList.rulesList as any)[ruleName];

      // create a new object from that rule class
      // pass to it the input name and the input value
      const rule = new Rule(this.input, this.value);

      // validate

      await rule.validate();

      // if fails
      // push the rule to errors list
      if (rule.fails()) {
        this.errorsList.push(rule.errors());
      }
    }
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
```

So far so gooood.

Now let's create our first rule, the `required` rule.

## Required Rule

The `required` rule is a rule that checks if the input is required or not.

Create `rules` directory (I told you earlier to create it, but sounds you forgot :P) now let's create `required.ts` file.

```ts
// src/core/validator/rules/required.ts
export default class RequiredRule {
  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Constructor
   */
  public constructor(protected input: string, protected value: any) {
    //
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
```

We'll stop at this point here so in our next article we'll start implementing the `required` rule and couple of other rules.

I hope you enjoy this series, and if you have any questions or suggestions, please let me know in the comments.

## 🎨 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 🎞️ Video Course (Arabic Voice)

If you want to learn this course in video format, you can find it on [Youtube](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), the course is in Arabic language.

## 💰 Bonus Content 💰

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

- [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
- [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
- [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

- [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
- [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
- [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

- [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

- [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)