Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

schemaglobin

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

schemaglobin

Validate user-entered data.

  • 4.0.6
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
1
Maintainers
1
Weekly downloads
 
Created
Source

Schemaglobin: Validate unknown user input against schemas

Semantic Release Conventional Commits Prettier GitHub Actions npm

Schemaglobin is a schema validator for user input written for JavaScript and TypeScript with special attention paid to TypeScript types.

Installation

npm install schemaglobin

Usage

Import schema creator functions (e.g. string()). These functions take an options object that configures the schema and returns an instance of the corresponding schema class.

When you have created a schema you can pass unknown values into the validate() method to validate those values:

  • Valid values will be returned unchanged.
  • Invalid values that can be trivially fixed will be modified and returned.
  • Invalid values will return instances of Invalid, which contains a user-facing message property describing why.

This basic example shows how Schemaglobin can be used in the real world:

import { object, string, number, boolean, Invalid } from "schemaglobin";

// Create a schema that can validate the data.
const schema = object(
	required: true;
	props: {
		"name": string({ required: true }),
		"age": number({ required: true, min: 0, max: 150 }),
		"status": boolean(),
	}
);

// Function that runs on the server that validates input and saves it to the database.
export function myServerFunction(unsafeInput: unknown): true {
	// Validate the unsafe input.
	const data = schema.validate(unsafeInput);

	// Note that the TypeScript type of data is:
	// { name: string, age: number, status: boolean } | Invalid

	// The data was invalid. Throw an error.
	if (data instanceof Invalid) throw new ServerError("Invalid input: " + data.message);

	// Note: Now that we've filtered out Invalid we know the type is just the expect data object.

	// The data is valid. Now we can save it to the database safely.
	const status = saveToDatabase(data);

	// Success!
	return true;
}

Invalid values

Is you pass an invalid value into validate() then two things might happen: 1) If the invalid value can be trivially converted to a valid value without data loss, it will be converted and returned, or 2) An instance of Invalid will be returned:

import { string, number, email, url, boolean, Invalid } from "schemaglobin";

// Trivial conversion.
boolean().validate("abc"); // Returns `true`
boolean().validate(""); // Returns `false`
string().validate(123); // Returns "123" string.
number().validate("123.456"); // Returns 123.456 number.

// Fully invalid values.
string().validate(true); // Returns Invalid("Must be string")
number().validate("abc"); // Returns Invalid("Must be number or null")
email().validate("abc"); // Returns Invalid("Invalid email format")
url().validate("abc"); // Returns Invalid("Invalid URL format")

With object schemas, options.props is used to fill (and trivially convert) missing object props:

import { object, number, string } from "schemaglobin";

// Make an object schema.
const schema = object({
	props: {
		name: string({ value: "DEFAULT" }),
		age: number(),
	},
});

// Returns { name: "DEFAULT", age: 123 }
schema.validate({ age: "123" });

// Returns { name: "Dave", age: null }
schema.validate({ name: "Dave" });

Instances of Invalid contain a string .message property describing the issue:

const invalid = url().validate("abc");
console.error(invalid.message); // Logs "Invalid URL format"

When validating an object, it's possible the contents might be invalid. In this situation Invalid also has a .messages object specifying where, within the object, the data was invalid.

import { object, string, number } from "schemaglobin"

// Make an object schema with `options.props`
const schema = object({
	props: {
		name: string({ required: true }),
		age: number({ min 0, max: 200 }),
	}
});

// Validate an invalid value.
const invalid = schema.validate({ age: 900 });

console.log(invalid.message); // "Invalid format"
console.log(invalid.messages); // { name: "Required", age: "Maximum 200" }

This also works for arrays. The keys in .messages will be numeric strings:

import { array, string } from "schemaglobin";

// Make an array schema with `options.items`
const schema = array({
	items: string({ required: true }),
});

// Validate an invalid value.
const invalid = schema.validate([123, true, ""]);

console.log(invalid.message); // "Invalid format"
console.log(invalid.messages); // { "1": "Must be string", "2": "Required" }

Validating different types

Schemaglobin contains a bunch of different schema types you can use:

import { boolean, string, number, date, email, phone, url, key, array, object, map } from "schemaglobin";

// Create schemas.
const booleanSchema = boolean({ required: true, ...etc });
const stringSchema = string({ required: true, ...etc });
const numberSchema = number({ required: true, ...etc });
const colorSchema = color({ required: true, ...etc });
const dateSchema = date({ required: true, ...etc });
const emailSchema = email({ required: true, ...etc });
const phoneSchema = phone({ required: true, ...etc });
const urlSchema = url({ required: true, ...etc });
const keySchema = key({ required: true, ...etc });
const arraySchema = array({ required: true, ...etc });
const objectSchema = object({ required: true, ...etc });
const mapSchema = object({ required: true, ...etc });

// Successful validation.
booleanSchema.validate(true); // Returns true
stringSchema.validate("abc"); // Returns "abc"
numberSchema.validate(12345); // Returns "123"
colorSchema.validate("#00CCFF"); // Returns "#00CCFF"
dateSchema.validate("1995"); // Returns "1995-01-01"
emailSchema.validate("me@x.com"); // Returns "me@x.com"
phoneSchema.validate("+1234567890"); // Returns "+1234567890"
urlSchema.validate("http://x.com"); // Returns "http://x.com"
keySchema.validate("ajdk29Jak"); // Returns "ajdk29Jak"
arraySchema.validate(["a", 2, true]); // Returns ["a", 2, true]
objectSchema.validate({ a: "A" }); // Returns { a: "A" }
mapSchema.validate({ a: "A" }); // Returns { a: "A" }

// Successful validation.
stringSchema.validate(true); // Returns Invalid("Must be string")
numberSchema.validate(true); // Returns Invalid("Must be number or null")
dateSchema.validate("aaaaaaa"); // Returns Invalid("Invalid date")
colorSchema.validate(true); // Returns Invalid("Must be string or null")
emailSchema.validate("111111"); // Returns Invalid("Invalid email format")
phoneSchema.validate("aaaaaa"); // Returns Invalid("Invalid phone number")
urlSchema.validate("11111111"); // Returns Invalid("Invalid URL format")
keySchema.validate("!!!!!!!"); // Returns Invalid("Invalid key format")
arraySchema.validate(true); // Returns Invalid("Must be array")
objectSchema.validate(true); // Returns Invalid("Must be object or null")
mapSchema.validate(true); // Returns Invalid("Must be object or null")

Default values

Every schema has a default value that is used when the value is undefined. The default value can be changed for any schema with options.value

import { string } from "schemaglobin";

const schemaWithoutDefault = string();
schemaWithDefault.validate(); // Returns ""

const schemaWithDefault = string({ value: "WOW VALUE" });
schemaWithDefault.validate(); // Returns "WOW VALUE"
schemaWithDefault.validate(undefined); // Returns "WOW VALUE"

Required values

Normally values are not required, meaning null or "" empty string are allowed. This can be changed with options.required

import { number } from "schemaglobin";

const optionalSchema = number({ required: false });
optionalSchema.validate(null); // Returns null.

const requiredSchema = number({ required: true });
optionalSchema.validate(null); // Returns Invalid("Required")

Using TypeScript

Schemaglobin pays special attention to the TypeScript type of values returned by validate(), for example:

  • NumberSchema.validate()
    • Normally returns number | null | Invalid
    • If options.required is truthy the value will never be null (as that would be invalid) so it only returns number | Invalid
  • StringSchema.validate()
    • Normally returns string | Invalid
    • If options.options is set it can return a more specific type, e.g. "a" | "b" | "" | Invalid
  • ObjectSchema.validate()
    • If flags.partial is set it returns a deep partial of the object (i.e. all props become optional).
import { object, string, number, boolean, Invalid } from "schemaglobin";

// `options.required` filters out falsy value.
const requiredNumber: number | Invalid = number({ required: true }).validate(123); // No error.
const optionalNumber: number | Invalid = number({ required: false }).validate(123); // Error `number | null | Invalid` cannot be assigned to `number | Invalid`

// Return type for StringSchema is inferred from `options.options`
const enumStringArray: "a" | "b" | Invalid = string({ options: ["a", "b"] }).validate("a"); // No error.
const enumStringObject: "a" | "b" | Invalid = string({ options: { a: "A", b: "B" } }).validate("a"); // No error.

// Return type for ObjectSchema is inferred from `options.props`
const objectSchema = object({
	required: true,
	props: {
		num: number(),
		str: string({ required: true }),
		bool: boolean({ required: true }),
	},
});

// Validated value has type `{ num: number | null, str: string, bool: true } | Invalid`
const obj = objectSchema.validate(undefined);
if (!(obj instanceof Invalid)) {
	const num: number | null = obj.num; // No error.
	const str: string = obj.str; // No error.
	const bool: true = obj.bool; // No error.
}

Schemaglobin also provides the SchemaType<Schema> helper type, which allow you to extract the type of a schema:

import { string, SchemaType } from "schemaglobin";

// Make some schemas.
const requiredStringSchema = string({ required: true });
const optionalStringSchema = string({ required: false });
const requiredEnumSchema = string({ required: true, options: ["a", "b"] });
const optionalEnumSchema = string({ required: false, options: ["a", "b"] });

// Extract the types.
type RequiredStringType = SchemaType<typeof requiredStringSchema>; // string
type OptionalStringType = SchemaType<typeof optionalStringSchema>; // string
type RequiredEnumType = SchemaType<typeof requiredEnumSchema>; // "a" | "b"
type OptionalEnumType = SchemaType<typeof optionalEnumSchema>; // "a" | "b" | ""

// Object type can be inferred from `options.props`
const objectSchema = object({ props: { str: string(), num: number() } });
type ObjectType = Schematype<typeof objectSchema>; // { str: string, num: number | null }

Validation flags

The second argument passed to validate() is an options bag of flags which can modify the validation behaviour. For example the flags.partial flag makes ObjectSchema validation partial (i.e. missing properties are not invalid).

import { object, string, number } from "schemaglobin";

const schema = object.required({{
	name: string.required,
	age: number.required,
	job: string.required,
});

// Normally a partial value is invalid...
const invalid = schema.validate({ name: "Dave", age: "35" });
console.log(invalid.message); // "Invalid format"
console.log(invalid.messages); // { job: "Must be string" }

// But with the partial flag the value is valid.
const valid = schema.validate({ name: "Dave", age: "35" });
console.log(valid); // { name: "Dave", age: 35 }

Reference

All schema creator functions allow the following options (and may allow others too):

  • options.title: string = "" - A title for the schema (for showing in a user-facing field).
  • options.description: string = "" - A description for the schema (for showing in a user-facing field).
  • options.placeholder: string = "" - A placeholder for the schema (for showing in a user-facing field).

boolean()

The boolean() creator function creates a BooleanSchema instance:

  • All truthy values are converted to true
  • All falsy values are converted to false
  • Default value is false
  • false is an invalid value if options.required is truthy.
  • Doesn't accept any additional options.

boolean() also allows the following options:

  • options.value: boolean = false - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then false values will return Invalid("Required")

string()

The string() creator function creates a StringSchema instance:

  • Strings are valid values.
  • Whitespace is trimmed automatically.
  • Control characters are stripped automatically.
  • Newlines (and tabs) are stripped unless the multiline option is true
  • Numbers are converted to string automatically.
  • Falsy values are converted to "" empty string
  • Default value is "" empty string
  • "" empty string is an invalid value if options.required is truthy.

string() also allows the following options:

  • options.value: string = "" - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then empty strings will return Invalid("Required")
  • options.min: number = 0 - The minimum number of characters allowed.
  • options.max: number = null - The maximum number of characters allowed.
  • options.options?: string[] | { string: string } - Explicit list of allowed values as either:
    1. An array of strings where each string is an allowed value.
    2. An object where each string key is an allowed value, and the corresponding value is a user-facing title for the option.
  • options.match: RegExp = null - A regular expression that the string must match.
  • options.multiline: boolean = false - Whether the string allows newlines or not

number()

The number() creator function creates a NumberSchema instance that can validate numbers:

  • Numbers are valid values.
  • 0 zero is a valid value.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

number() also allows the following options:

  • options.value: number | null = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")
  • options.min: number = null - The minimum number allowed.
  • options.max: number = null - The maximum number allowed.
  • options.options: number[] | { number: string } = null - Explicit list of allowed values as either:
    1. An array of numbers where each number is an allowed value.
    2. An object where each number key is an allowed value and the corresponding value can be a user-facing title for the option.
  • options.step: number = null - The step size for the the number (the value will be rounded to the closest step).

date()

The date() creator function creates a DateSchema instance that can validate date YMD strings:

  • Strings in YMD format are valid, e.g. 1995-10-20
  • Whitespace is trimmed automatically.
  • Strings in other formats are parsed with new Date() and converted to YMD strings.
  • Date instances and numbers are converted to YMD strings.
  • value, min and max options can be functions that return calculated values, e.g. using value: Date.now will set the value to today's date.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

date() also allows the following options:

  • options.value: Date = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")
  • options.min: string = null - The minimum date allowed.
  • options.max: string = null - The maximum date allowed.

color()

The color() creator function creates a ColorSchema instance that can validate hexadecimal color strings:

  • Strings in hex color format are valid, e.g. #00CCFF
  • Whitespace is trimmed automatically.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

color() also allows the following options:

  • options.value: Date = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")

email()

The email() creator function creates a EmailSchema instance that can validate email addresses:

  • Strings that are valid email addresses are valid, e.g. dave@gmail.com
  • Whitespace is trimmed automatically.
  • Email is converted to lowercase automatically.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

email() also allows the following options:

  • options.value: string = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")

url()

The url() creator function creates a UrlSchema instance that can validate URLs:

  • Strings that are valid email addresses are valid, e.g. https://x.com or data:anything
  • Whitespace is trimmed automatically.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy

url() also allows the following options:

  • options.value: string = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")
  • options.schemes: string[] = ["http:", "https:"] - Whitelist of allowed URL schemes.
  • options.hosts: string[] = null - List of allowed hostnames, e.g. ["google.com"]
  • options.max: number = 512 - Maximum length of a URL.

phone()

The phone() creator function creates a PhoneSchema instance that can validate URLs:

  • Strings that are valid phone numbers are valid, e.g. +441234567890
  • Whitespace is trimmed automatically.
  • Non-digit characters are stripped automatically.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

phone() also allows the following options:

  • options.value: string = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")

key()

The key() creator function creates a KeySchema instance that can validate database key strings:

  • Strings that are valid database keys are valid, e.g. abc or AAAA1234
  • By default key strings can only contain a-zA-Z0-9 or - hyphen.
  • Whitespace is trimmed automatically.
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

key() also allows the following options:

  • options.value: string = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")
  • options.match: RegExp = /[a-zA-Z0-9-]{1,24}/ - Format the database key must match.

object()

The object() creator function creates a ObjectSchema instance that can validate an exact object:

  • Objects are valid, e.g. { a: 1, b: "two" }
  • Contents of the object can be validated with options.props
  • Properties not specified in `options.props
  • Falsy values are converted to null
  • null is an invalid value if options.required is truthy.

object() also allows the following options:

  • options.value: {} | null = null - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then null values will return Invalid("Required")
  • options.props: { [prop: string]: Schema } (required) - An object explicitly specifying the type of each individual property.

validate() on ObjectSchema instances supports the following flags:

  • flags.partial - Validate a partial object, i.e. everything in options.props becomes optional, and missing props are not added to the returned value (works deeply/recursively too because flags is passed down to subschemas).

array()

The array() creator function creates a ArraySchema instance that can validate arrays and their contents:

  • Arrays are valid, e.g. [1,2,3]
  • Contents of the array can be validated with options.items
  • Falsy values are converted to [] empty array.
  • [] empty array is an invalid value if options.required is truthy.

array() also allows the following options:

  • options.value: [] = [] - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then empty arrays will return Invalid("Required")
  • options.min: number = null - The minimum number of items allowed.
  • options.max: number = null - The maximum number of items allowed.
  • options.items: Schema (required) - Schema that will be used to validate the contents of the array.

map()

The map() creator function creates a MapSchema instance that can validate an object containing a list of key: value entries:

  • Objects are valid, e.g. { a: 1, b: 2, c: 3 }
  • Contents of the object can be validated with options.props
  • Falsy values are converted to {} empty object.
  • {} empty object is an invalid value if options.required is truthy.

map() also allows the following options:

  • options.value: {} = {} - The default value which will be used if the value is undefined
  • options.required: boolean = false - If true, then empty objects will return Invalid("Required")
  • options.min: number = null - The minimum number of items allowed.
  • options.max: number = null - The maximum number of items allowed.
  • options.items: Schema (required) - Schema that will be used to validate all properties in the object.

Shortcuts

The following static values are available as shortcuts attached to the creator functions for all simple values:

import { boolean, date, email, key, number, phone, string, url } from "schemaglobin";

// The following is equivalent to e.g. boolean({ required: true }).validate()
boolean.required.validate(true);
boolean.optional.validate(false);
date.required.validate(new Date());
date.optional.validate(null);
email.required.validate("dave@x.com");
email.optional.validate(null);
key.required.validate("abc123");
key.optional.validate(null);
number.required.validate(12345);
number.optional.validate(null);
number.timestamp.validate(Date.now());
phone.required.validate("+44123456789");
phone.optional.validate(null);
string.required.validate("AAAAA");
string.optional.validate("");
url.required.validate("https://x.com");
url.optional.validate(null);
color.required.validate("#00CCFF");
color.optional.validate(null);

Object/map/array schemas also provide shortcuts, but as options.props and options.items are required these must be passed in as the only argument:

import { object, array, map, number, string, boolean } from "schemaglobin";

// Object shortcuts accept `options.props` as an argument.
object.required({ num: number.optional }).validate({ num: 123 });
object.optional({ num: number.required }).validate(null);

// Array shortcuts accept `options.items` as an argument.
array.required(string.required).validate([1, 2, 3]);
array.optional(string.required).validate([]);

// Map shortcuts accept `options.items` as an argument.
map.required(boolean.required).validate({ a: 1, b: 2, c: 3 });
map.optional(boolean.required).validate(null);

Changelog

See Releases

FAQs

Package last updated on 23 May 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc