Skip to main content
Deno 2 is finally here 🎉️
Learn more

🦩 Funval

A minimalist library for data validation using functions interfaces.

View On NPM Build Status Dependency Status Coverage Status License


Funval is a minimalist validation library that seamlessly integrates with your business logic. Using only plain functions, Funval knows how to validate your data and automatically generates TypeScript interfaces to reduce code duplications and complexity.

function ValidNumber(input: unknown): number {
  const value = Number(input);

  if (isNaN(value)) {
    throw new TypeError('Invalid number');
  }
  
  return value;
}

Main Features

  • Easy to Read - Uses functions as types (including String, Number, Boolean, etc…)
  • Reusable Interfaces - Create new validator using plain functions in seconds.
  • TypeScript Validation - Detect errors during compile time.
  • Function Composition - Pipe multiple validators to generate new ones.
  • Asynchronous & Synchronous Support - Automatically detected promises.
  • Pure Javascript - Also works without TypeScript.

Install

npm i funval

Usage

import { Schema, Optional, Or, Type, Integer, Between } from 'funval';

const UserSchema = {
  name: Optional(String),
  username: /^[a-z0-9]{3,10}$/,
  status: Or('active' as 'active', 'suspended' as 'suspended'),
  amount: input => Between(1, 50)(Integer(input)),
};

const validator = Schema(UserSchema);

let user: Type<typeof UserSchema>;

try {
  user = validator({
    username: 'john1',
    // @ts-ignore Type '"unregistered"' is not assignable to type '"active" | "suspended"'.
    status: 'unregistered',
    amount: 20,
  });
} catch (err) {
  console.error(err.message, err.paths);
}

Creating Validators

A validator is any function that can return a value without throwing any exceptions:

import * as EmailValidator from 'email-validator';

function Email(input: string): string {
  if (!EmailValidator.validate(input)) {
    throw new TypeError(`Invalid email address: "${input}"`);
  }

  return input;
}

You can use the above validator on schemas as an Email type:

const UserSchema = {
  email: Email,
};

const validator = Schema(UserSchema);

Asynchronous Validators

Asynchronous validators are supported by returning a Promise (or PromiseLike) values:

import fetch from 'node-fetch';

async function AvailableUsername(input: string): Promise<string> {
  const res = await fetch(`/check-username?username=${encodeURIComponent(input)}`);

  if (!res.ok) {
    throw new TypeError(`Username "${input}" is already taken`);
  }

  return input;
}

Funval automatically detects promise and convert the return type of the Validator to promise as well:

const UserSchema = {
  username: AvailableUsername,
};
const validator = Schema(UserSchema);

const user = await validator({ username: 'test' });

If you prefer, you can safely convert any validator to an asynchronous validator using the Async helper:

import { Async, Schema } from 'funval';

const UserSchema = {
  email: Email,
};
const validator = Async(Schema(UserSchema));

// will catch instead of throwing
validator({ email: 'invalid-email' }).catch(err => console.err(err));

Available Validators

All the available validators ordered by type:


Schema

import { Schema } from 'funval';

declare function Schema(schema: any, error?: string | Error): Validator;

Validate inputs using the given schema.

Throws: If the input does not match the given schema.


Maybe

import { Maybe } from 'funval';

declare function Maybe(schema: any, error?: string | Error): Validator;

Same as Schema but ignores undefined or omitted inputs.

Throws: If the input tested and does not match the given schema.


Optional

import { Optional } from 'funval';

declare function Optional(schema: any, error?: string | Error): Validator;

Same as Schema but ignores undefined, null or omitted inputs.

Throws: If the input tested and does not match the given schema.


Default

import { Default } from 'funval';

declare function Default(schema: any, value: any): Validator;

Validate inputs using the given schema or use the given value if input equals to undefined or omitted.

Throws: If the input tested and does not match the given schema.


Required

import { Required } from 'funval';

declare function Required(schema: any, error?: string | Error): Validator;

Validate inputs using the given schema.

Throws: If input is undefined or does not match the given schema.


Truthy

import { Truthy } from 'funval';

declare function Truthy<T>(input: T): T;

Validate input is truthy.

Throws: If input is not truthy.


Or

import { Or } from 'funval';

declare function Or(...candidates: any[]): Validator;

Validate inputs using any of the given candidates.

Throws: If the input does not match to any of the given schemas.


ArrayOf

import { ArrayOf } from 'funval';

declare function ArrayOf(itemSchema: any, error?: string | Error): Validator;

Validate the input is an array and that each item matches the given schema.

Throws: If the given input is not an array or one of the items not matches the item schema.


TypeOf

import { TypeOf } from 'funval';

declare function TypeOf(
  typeOf:
    | 'string'
    | 'number'
    | 'object'
    | 'boolean'
    | 'undefined'
    | 'symbol'
    | 'bigint',
  error?: string | Error,
): Validator;

Check the input type is equals to the given type.

Throws: If the input type is different from the given type.


Any

import { Any } from 'funval';

declare function Any(input: any): any;

Allow any type of input.

Throws: Never.


Override

import { Override } from 'funval';

declare function Override(value: any): Validator;

Override any input and returns the given value.

Throws: Never.


Test

import { Test } from 'funval';

declare function Test<T>(test: (input: T) => unknown, error?: string | Error): Validator;

Return the input value as is, only if the test function returns a truthy value.

Throws: If the test function throws or the test function return a non-truthy value.


Equals

import { Equals } from 'funval';

declare function Equals(value: any, error?: string | Error): Validator;

Check the input is strictly equals to the given value.

Throws: If the input does not equal to the given value.


GreaterThan

import { GreaterThan } from 'funval';

declare function GreaterThan(value: any, error?: string | Error): Validator;

Check the input is greater than the given value.

Throws: If the input does not greater than the given value.


GreaterThanEqual

import { GreaterThanEqual } from 'funval';

declare function GreaterThanEqual(value: any, error?: string | Error): Validator;

Check the input is greater than or equals the given value.

Throws: If the input does not greater than or equals the given value.


LessThan

import { LessThan } from 'funval';

declare function LessThan(value: any, error?: string | Error): Validator;

Check the input is less than the given value.

Throws: If the input does not less than the given value.


LessThanEqual

import { LessThanEqual } from 'funval';

declare function LessThanEqual(value: any, error?: string | Error): Validator;

Check the input is less than or equals the given value.

Throws: If the input does not less than or equals the given value.


Between

import { Between } from 'funval';

declare function Between(minValue: any | null, maxValue: any | null, error?: string | Error):
Validator;

Check the input is between the given boundaries.

Throws: If the input is not between the given boundaries.


ContentString

import { ContentString } from 'funval';

declare function ContentString(input: unknown): string;

Converts any input to a valid string.

Throws: If input is either null, undefined, an object without proper toString implementation, empty or a whitespace string.


TrimString

import { TrimString } from 'funval';

declare function TrimString(input: unknown): string;

Same as ContentString, but trim the output as well.

Throws: If input is either null, undefined, an object without proper toString implementation, empty or a whitespace string.


StringRange

import { StringRange } from 'funval';

declare function StringRange(
  minLength: number | null,
  maxLength: number | null,
  error?: string | Error,
): Validator;

Converts any input to a valid string and make sure the string is in the given boundaries.

Throws: If input is either null, undefined, an object without proper toString implementation, empty string, whitespace string, or string outside of the given boundaries.


StringMatch

import { StringMatch } from 'funval';

declare function StringMatch(regex: RegEx, error?: string | Error): Validator;

Validate the input is matches the given regular expression.

Throws: If input does not match the given regular expression.


Float

import { Float } from 'funval';

declare function Float(input: unknown): number;

Converts any input to a valid float number.

Throws: If the input can not convert to a valid number.


Integer

import { Integer } from 'funval';

declare function Integer(input: unknown): number;

Converts any input to a valid integer number.

Throws: If the input can not convert to a valid number or the number is not an integer.


Bool

import { Bool } from 'funval';

declare function Bool(input: unknown): boolean;

Converts any input to a valid boolean.

Throws: If the input can not convert unambiguously to a boolean.


License

MIT license © 2020 Neuledge