23-Nodejs Course 2023: Validation Part I

hassanzohdy

Hasan Zohdy

Posted on November 2, 2022

23-Nodejs Course 2023: Validation Part I

So far so good, now let's move to another important topic: Validation.

Validation

Well, you'll start thinking now, what he will use for validation?, exactly as you thought, we'll do it from scratch xD, why? because its fun.

Why Validation?

Any application will receive data from the user, and that data will be stored in the database, and that data will be used to perform some action, so it is important to validate that data, to ensure that it is correct, and that it is not malicious.

How it works

The scenario will be like this:

  • We attach the route handler to the route.
  • If the route has a validation static property, then the request handler will start making the validation.
  • If the validation fails, then the request handler will return an error.
  • If the validation succeeds, then the request handler will be executed.

We'll do a simple validator that we can attach to the handler function the controller function a static variable we'll call it validation this will contain a rules object and we'll use it like this:

// src/apps/users/controllers/create-user.ts

export default function createUser(request) {
  const { name, email } = request.body;

  return {
    name,
    email
  }
}

createUser.validation = {
  rules: {
    name: ['required', 'string'],
    email: ['required', 'email'],
  }
};
Enter fullscreen mode Exit fullscreen mode

And that it!

Now let's start create our simple validator.

Request handler

If you remember we created earlier a Request class that handle the request, which has an execute method that calls our handler.

That executer we'll check inside it if the handler has a validation property, if so then we'll get it and start validating the request inputs in these rules.

If the validation passes, then we'll call the handler, if not we'll throw an error.

// src/core/http/request.ts
  /**
   * Execute the request
   */
  public async execute() {
    if (this.handler.validation) {
      const validationResult = new Validator(this, this.handler.validation);

      validationResult.scan();

      if (!validationResult.passes()) {
        return this.response.status(422).send(validationResult.errors());
      }
    }

    return await this.handler(this, this.response);
  }
Enter fullscreen mode Exit fullscreen mode

We checked if the handler has a validation property, if so then we call the Validator class to validate the request inputs, this class will receive all inputs and the rules.

Validator class

Now let's make the validator class, create validator folder inside src/core and create index.ts file inside it.

// src/core/validator/index.ts
import { Request } from "core/http/request";

export default class Validator {
  /**
   * Validation errors
   */
  protected errorsList: any = [];

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

  /**
   * Scan all the rules and validate the request
   */
  public scan() {}

  /**
   * Determine if the validation passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Determine if the validation fails
   */
  public fails() {
    return !this.passes();
  }

  /**
   * Get errors list
   */
  public errors() {
    return this.errorsList;
  }
}
Enter fullscreen mode Exit fullscreen mode

Here we added errorsList as an array that will contain all errors list, the constructor receives the request and the rules, and we added scan method that will scan all the rules and validate the request, and we added passes and fails methods that will determine if the validation passes or fails.

Now let's start the validation process.

If you're wondering why we're using readonly because it do what is says, we don't want to modify it, we just want to read it.

If you're wondering why we're using protected because we may make it in the future to be extended.

Validation process

First we'll get all the inputs from the request that needs to be validated, and we'll loop over the rules and validate each input.

So we only need to get the input values for these rules, let's make a new method called only in the request class that will return the input values for these rules.

// src/core/http/request.ts
import { only } from "@mongez/reinforcements";

// class Request {
  // ..
  /**
   * Get only the given keys from the request
   */
  public only(keys: string[]) {
    return only(this.all(), keys);
  }
Enter fullscreen mode Exit fullscreen mode

We used only utility to get only these given keys from the request.

Now let's head back to the validator class and get the inputs that needs to be validated.

// src/core/validator/index.ts

  /**
   * Scan all the rules and validate the request
   */
  public scan() {
    // get the request data for the rules inputs
    const inputValues = this.request.only(Object.keys(this.rules));

    // loop through the rules
    for (const input in this.rules) {
      // get the value for the input
      const inputValue = inputValues[input];
      // get the rules for the input
      const inputRules = this.rules[input];

      // Create a new rule handler to validate the input
      const rule = new Rule(input, inputValue, inputRules);

      // if the rule validation fails
      if (rule.validate().fails()) {
        // then add the rule errors to the errors list
        this.errorsList.push(rule.errors());
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

I've put comments per each line i wrote above, first we'll get the input values directly from the request so we don't make multiple calls to the request class, then we made a loop over the rules (Remember it is an object not an array) so we used for.in loop to get the input as as the key, next we got the following data:

  1. The input value
  2. The input rules

Then we created a new Rule class to validate the input, and we called the validate method on it, if the validation fails then we'll add the errors to the errors list.

Rule class

Now let's make the rule class, create rule.ts file inside src/core/validator folder.

// src/core/validator/rule.ts
export default class Rule {
  /**
   * Errors list
   */
  protected errorsList: any = [];

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

We'll stop at this point of the tutorial, and we'll continue in the next part.

Conclusion

In this article we covered the concept behind how we would implement the validation process, and we made a simple validator that can validate the request inputs.

🎨 Project Repository

You can find the latest updates of this project on Github

😍 Join our community

Join our community on Discord 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, 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

Packages & Libraries

React Js Packages

Courses (Articles)

💖 💪 🙅 🚩
hassanzohdy
Hasan Zohdy

Posted on November 2, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related