DEV Community

Hasan Zohdy
Hasan Zohdy

Posted on

25-Nodejs Course 2023: Validation Part III: Rule Implementation

Now we're going to continue working on our Rules, let's start with the required rule.

Required Rule

Open src/core/validator/rules/required.ts and let's update it with the following code:

// src/core/validator/rules/required.ts
export default class RequiredRule {
  /**
   * Rule name
   */
  public static ruleName = "required";

  /**
   * Determine if rule is valid
   */
  protected isValid = true;

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


  /**
   * Validate the rule
   */
  public async validate() {
    this.isValid = this.value.length > 0;
  }
}
Enter fullscreen mode Exit fullscreen mode

We added ruleName as static property which holds the rule name, why we would do such a thing? because we might later customize the rulesTypes property in the Rules list class to be more dynamically.

Then we constructed the rule with the input name and its value.

isValid property will be true by default, in our validate method we'll update it to determine if we have a valid value or not.

Now let's implement the validate method:

// src/core/validator/rules/required.ts
export default class RequiredRule {
  /**
   * Rule name
   */
  public static ruleName = "required";

  /**
   * Determine if rule is valid
   */
  protected isValid = true;

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

  /**
   * Validate the rule
   */
  public async validate() {
    this.isValid = Boolean(this.value) && this.value.length > 0;
  }
}
Enter fullscreen mode Exit fullscreen mode

And that it!, we just checked if there is a value and the value length is higher than 0, if it is then we have a valid value, otherwise we don't.

Now let's update update the rest of methods of the rule

// src/core/validator/rules/required.ts
export default class RequiredRule {
  /**
   * Rule name
   */
  public static ruleName = "required";

  /**
   * Determine if rule is valid
   */
  protected isValid = true;

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

  /**
   * Validate the rule
   */
  public async validate() {
    this.isValid = Boolean(this.value) && this.value.length > 0;
  }

  /**
   * Determine if rule validation passes
   */
  public passes() {
    return this.isValid;
  }

  /**
   * Determine if rule validation fails
   */
  public fails() {
    return this.isValid === false;
  }

  /**
   * Get error message
   */
  public error() {
    return `${this.input} is required`;
  }
}
Enter fullscreen mode Exit fullscreen mode

We added the passes method which will check for the isValid property, if it's true then the rule passes, otherwise it fails.

We added the fails method which will check for the isValid property, if it's false then the rule fails, otherwise it passes.

We added the error method which will return the error message using the input name.

Now let's give it a try.

Open src/app/users/controllers/create-user and let's update it with the following code:

// src/app/users/controllers/create-user.ts
import { Request } from "core/http/request";

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

  return {
    name,
  };
}

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

Now let's try to send a request without the name and email fields, we should get the following output:

Result

As you can see its a merged list of errors, let's update it to make it more readable.

Back to Rules List

Now let's go back to our rules list class, what we will do is that if the rule has an error, we will push it to the rules list, and the rules list itself will return an object, input which will contain the input name and errors which will contain the errors list.

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

export default class RulesList {
  /**
   * Rules list
   */
  public static rulesTypes = {
    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;
  }

  /**
   * Validate the rules
   */
  public async validate() {
    for (const ruleName of this.rules) {
      const RuleClass = (RulesList.rulesTypes as any)[ruleName];

      const rule = new RuleClass(this.input, this.value);

      await rule.validate();

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

  /**
   * 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 {
      input: this.input,
      errors: this.errorsList,
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Now our errors will be like this

Errors Result

Pretty neat right?

String Rule

Let's make another rule to make sure we're working right, we'll make a rule to check if the input is a string.

Create src/core/validator/rules/string.ts.

We need to install a new package actually, it will help us a lot to validate values Supportive Is.

yarn add @mongez/supportive-is
Enter fullscreen mode Exit fullscreen mode

Now let's update our string.ts file with the following code:

// src/core/validator/rules/string.ts
import Is from "@mongez/supportive-is";

export default class StringRule {
  /**
   * Rule name
   */
  public static ruleName = "string";

  /**
   * Determine if rule is valid
   */
  protected isValid = true;

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

  /**
   * Validate the rule
   */
  public async validate() {
    this.isValid = Is.string(this.value);
  }

  /**
   * Determine if rule validation passes
   */
  public passes() {
    return this.isValid;
  }

  /**
   * Determine if rule validation fails
   */
  public fails() {
    return this.isValid === false;
  }

  /**
   * Get error message
   */
  public error() {
    return `${this.input} is not a string`;
  }
}
Enter fullscreen mode Exit fullscreen mode

What we updated is the validate method, we used the Is.string method to check if the value is a string or not.

And also updated the error message.

Now let's update our rulesTypes and add the string rule.

// src/core/validator/rules-list.ts
import { Request } from "core/http/request";
import RequiredRule from "./rules/required";
import StringRule from "./rules/string";

export default class RulesList {
  /**
   * Rules list
   */
  public static rulesTypes = {
    required: RequiredRule,
    string: StringRule,
  };
Enter fullscreen mode Exit fullscreen mode

Now let's update our rules in create user controller.

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

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

Now let's run again the server.

Now you'll see something like this:

Errors List

Conclusion

In this article, we learned how to create a new rule, we created two rules, required and string, we also learned how to use the rules in our controllers.

In our next article we'll enhance our validator, for example if we have added a rule that does not exist in the rulesTypes what should be happen.

Also we'll create a configuration file to handle validation configuration, for example what to return, do we need to return only the first error or all errors.

🎨 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)

Latest comments (0)