DEV Community

loading...
Cover image for Using Validate.js via a fluent  TypeScript interface

Using Validate.js via a fluent TypeScript interface

koistya profile image Konstantin Tarkus ・2 min read

When it comes to user input validation, particularly when designing (RESTful, GraphQL) APIs, it's important to have these objectives in mind:

  • Validator needs to support various data types such as emails, mobile phone numbers in different locales, credit cards, etc. Well tested and already used in production by many companies.
  • It needs to have an API (interface) that allows writing validation rules with the minimum amount of boilerplate code.
  • This interface needs to be strongly typed, allowing a better development experience (DX) when used with TypeScript.

I think validator.js (✭17k) fits perfectly into the first requirement. And, in order to make it less verbose, I would suggest using the following API (interface):

import { validate } from "validator-fluent";

const input = {
  givenName: "John",
  familyName: "Doe",
  email: "john@example.com",
  age: "18",
  website: "",
};

const [data, errors] = validate(input, (value) => ({
  given_name: value("givenName")
    .notEmpty()
    .isLength({ min: 3, max: 25 }),
  family_name: value("familyName")
    .notEmpty()
    .isLength({ min: 1, max: 25 }),
  email: value("email")
    .notEmpty()
    .isEmail(),
  age: value("aga").toNumber(),
  website: value("website").isURL(),
});

if (Object.keys(errors).length === 0) {
  await db.table("customer").insert(data);
}
Enter fullscreen mode Exit fullscreen mode

Validation Only (mode)

Often, it also needs to be possible to validate user input without saving it into the database. Example:

const input = {
  email: "john@example.com"
};

// Validate user input and exit
const dryRun = true;

const [data, errors] = validate(input, (value) => ({
  email_address: value("email")
    .notEmpty({ if: !dryRun })
    .isEmail(),
});

if (!dryRun && Object.keys(errors).length === 0) {
  await db.table("customer").insert(data);
}
Enter fullscreen mode Exit fullscreen mode

Adding Custom Rules

You can extend the built-in validator class with any additional validation rules (methods).

import { Validator } from "validator-fluent";

export class CoolValidator<K, V> extends Validator<K, V> {
  constructor(key: K, value: V) {
    super(key, value);
  }

  isLegit(): this {
    if (!this.isEmpty && this.value !== "legit") {
      this.errors.push("Not legit.");
    }

    return this;
  }
}
Enter fullscreen mode Exit fullscreen mode
import { validate, ValidationError } from "validator-fluent";

const input = { name: "???" };

const [data, errors] = validate(input, CoolValidator, (value) => ({
  name: value("name").notEmpty().isLegit(),
}));

if (Object.key(errors).length > 0) {
  throw new ValidationError(errors);
}
Enter fullscreen mode Exit fullscreen mode

References

Discussion (0)

Forem Open with the Forem app