DEV Community

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

Posted on

1 1

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

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up