DEV Community 👩‍💻👨‍💻

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

Posted on

Using Validate.js via a fluent TypeScript interface

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

Top comments (0)

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

⭐️🎀 JavaScript Visualized: Promises & Async/Await

async await