Skip to content

TypeBox ​

@feathersjs/typebox allows to define JSON schemas with TypeBox, a JSON schema type builder with static type resolution for TypeScript.

Note

For additional information also see the TypeBox documentation.

Usage ​

The module exports all of TypeBox functionality with additional support for query schemas and validators. The following is an example for defining the message schema from the guide using TypeBox:

ts
import { Type } from '@feathersjs/typebox'
import type { Static } from '@feathersjs/typebox'

const messageSchema = Type.Object(
  {
    id: Type.Number(),
    text: Type.String(),
    createdAt: Type.Number(),
    userId: Type.Number()
  },
  { $id: 'Message', additionalProperties: false }
)

type Message = Static<typeof messageSchema>

Result and data schemas ​

A good approach to define schemas in a Feathers application is to create the main schema first. This is usually the properties that are in the database and things like associated entries. Then we can get the data schema by e.g. picking the properties a client submits using Type.Pick

ts
import { Type } from '@feathersjs/typebox'
import type { Static } from '@feathersjs/typebox'

const userSchema = Type.Object(
  {
    id: Type.Number(),
    email: Type.String(),
    password: Type.String(),
    avatar: Type.Optional(Type.String())
  },
  { $id: 'User', additionalProperties: false }
)
type User = Static<typeof userSchema>

// Pick the data for creating a new user
const userDataSchema = Type.Pick(userSchema, ['email', 'password'])

type UserData = Static<typeof userDataSchema>

const messageSchema = Type.Object(
  {
    id: Type.Number(),
    text: Type.String(),
    createdAt: Type.Number(),
    userId: Type.Number(),
    // Reference the user
    user: Type.Ref(userSchema)
  },
  { $id: 'Message', additionalProperties: false }
)

type Message = Static<typeof messageSchema>

// Pick the data for creating a new message
const messageDataSchema = Type.Pick(messageSchema, ['text'])

type MessageData = Static<typeof messageDataSchema>

Query schemas ​

querySyntax ​

querySyntax(definition, extensions, options) returns a schema to validate the Feathers query syntax for all properties in a TypeBox definition.

ts
import { querySyntax } from '@feathersjs/typebox'

// Schema for allowed query properties
const messageQueryProperties = Type.Pick(messageSchema, ['id', 'text', 'createdAt', 'userId'], {
  additionalProperties: false
})
const messageQuerySchema = querySyntax(messageQueryProperties)

type MessageQuery = Static<typeof messageQuerySchema>

Additional special query properties that are not already included in the query syntax like $ilike can be added like this:

ts
import { querySyntax } from '@feathersjs/typebox'

// Schema for allowed query properties
const messageQueryProperties = Type.Pick(messageSchema, ['id', 'text', 'createdAt', 'userId'], {
  additionalProperties: false
})
const messageQuerySchema = Type.Intersect(
  [
    // This will additionally allow querying for `{ name: { $ilike: 'Dav%' } }`
    querySyntax(messageQueryProperties, {
      name: {
        $ilike: Type.String()
      }
    }),
    // Add additional query properties here
    Type.Object({})
  ],
  { additionalProperties: false }
)

To allow additional query properties outside of the query syntax use the intersection type:

ts
import { querySyntax } from '@feathersjs/typebox'

// Schema for allowed query properties
const messageQueryProperties = Type.Pick(messageSchema, ['id', 'text', 'createdAt', 'userId'], {
  additionalProperties: false
})
const messageQuerySchema = Type.Intersect(
  [
    querySyntax(messageQueryProperties),
    Type.Object({
      isActive: Type.Boolean()
    })
  ],
  { additionalProperties: false }
)

type MessageQuery = Static<typeof messageQuerySchema>

queryProperty ​

queryProperty(definition) returns a schema for the Feathers query syntax for a single property.

Validators ​

The following functions are available to get a validator function from a TypeBox schema.

note

See the validators chapter for more information on validators and validator functions.

getDataValidator ​

getDataValidator(definition, validator) returns validators for the data of create, update and patch service methods. You can either pass a single definition in which case all properties of the patch schema will be optional or individual validators for create, update and patch.

ts
import { Ajv } from '@feathersjs/schema'
import { Type, getDataValidator } from '@feathersjs/typebox'
import type { Static } from '@feathersjs/typebox'

const userSchema = Type.Object(
  {
    id: Type.Number(),
    email: Type.String(),
    password: Type.String(),
    avatar: Type.Optional(Type.String())
  },
  { $id: 'User', additionalProperties: false }
)
type User = Static<typeof userSchema>

// Pick the data for creating a new user
const userDataSchema = Type.Pick(userSchema, ['email', 'password'])

const dataValidator = new Ajv()

const userDataValidator = getDataValidator(userDataSchema, dataValidator)

// For more granular control
const userDataValidator = getDataValidator(
  {
    create: userDataSchema,
    update: userDataSchema,
    patch: Type.Partial(userDataSchema)
  },
  dataValidator
)

getValidator ​

getValidator(definition, validator) returns a single validator function for a TypeBox schema.

ts
import { Ajv } from '@feathersjs/schema'
import { Type, getValidator } from '@feathersjs/typebox'

// Schema for allowed query properties
const messageQueryProperties = Type.Pick(messageSchema, ['id', 'text', 'createdAt', 'userId'], {
  additionalProperties: false
})
const messageQuerySchema = querySyntax(messageQueryProperties)
type MessageQuery = Static<typeof messageQuerySchema>

// Since queries can be only strings we can to coerce them
const queryValidator = new Ajv({
  coerceTypes: true
})

const messageQueryValidator = getValidator(messageQuerySchema, queryValidator)

Validating Dates ​

When validating dates sent from the client, the most spec-compliant solution is to use the ISO8601 format. For example, SQLite date values are strings in the ISO8601 format, which is YYYY-MM-DDTHH:MM:SS.SSS. The character between the date and time formats is generally specified as the letter T, as in 2016-01-01T10:20:05.123. For date values, you implement this spec with Type.String and not Type.Date.

When using AJV you can validate this format with the ajv-formats package, which the Feathers CLI installs for you. Using it with @feathersjs/typebox looks like this:

ts
const userSchema = Type.Object(
  {
    createdAt: Type.String({ format: 'date-time' })
  },
  { $id: 'User', additionalProperties: false }
)

See the @feathersjs/mongodb docs for more information on validating dates with MongoDB.

Types ​

TypeBox provides a set of functions that allow you to compose JSON Schema similar to how you would compose static types with TypeScript. Each function creates a JSON schema fragment which can compose into more complex types. The schemas produced by TypeBox can be passed directly to any JSON Schema-compliant validator, or used to reflect runtime metadata for a type.

Standard ​

These are the standard TypeBox types. Each section shows equivalent code in three formats:

  • TypeBox
  • TypeScript type
  • JSON Schema

The following information comes from the TypeBox documentation. It has been formatted to make it easier to copy/paste examples.

Primitive Types ​

Primitive type utilities create schemas for individual values.

Any ​

Creates a schema that will always pass validation. It's the equivalent of TypeScript's any type.

js
const T = Type.Any()
js
type T = any
js
const T = {}
Unknown ​

Similar to any, it creates a schema that will always pass validation. It's the equivalent of TypeScript's unknown type.

js
const T = Type.Unknown()
js
type T = unknown
js
const T = {}
String ​

Creates a string schema and type. Type.String will generally be used for validating dates sent from clients, as well. See Validating Dates.

js
const T = Type.String()
js
type T = string
js
const T = {
  type: 'string'
}
String Formats Bundled ​

Strings are the most versatile, serializable type which can be transmitted from clients. Because of their versatility, several custom string formatters are supported, by default, in Feathers CLI-generated applications. Additional formats can be manually enabled.


date-time ​
ts
Type.String({ format: 'date-time' })

Validates against the date-time described in RFC3339/ISO8601, which is the following format:

YYYY-MM-DDTHH:MM:SS.SSS+HH:MM
2022-11-30T11:21:44.000-08:00

The sections of this format are described as follows:

  • full-date: YYYY-MM-DD
  • partial-time: HH:MM:SS.SSS (where .SSS represents optional milliseconds)
  • time-offset: +HH:MM (where + can be - and which value represents UTC offset or "time zone") required

time ​
ts
Type.String({ format: 'time' })

Validates against the following format:

HH:MM:SS.SSS+HH:MM
11:21:44.000-08:00

The sections of this format are described as follows:

  • partial-time: HH:MM:SS.SSS (where .SSS represents optional milliseconds)
  • time-offset: +HH:MM (where + can be - and which value represents UTC offset or "time zone") optional

date ​
ts
Type.String({ format: 'date' })

Validates against the full date described in RFC3339/ISO8601, which is the following format:

YYYY-MM-DD
2022-11-30

email ​
ts
Type.String({ format: 'email' })

Validates email addresses against the format specified by RFC 1034.


hostname ​
ts
Type.String({ format: 'hostname' })

Validates hostnames against the format specified by RFC 1034.


ipv4 ​
ts
Type.String({ format: 'ipv4' })

Validates an IPV4-formatted IP Address.

0.0.0.0 to 255.255.255.255

ipv6 ​
ts
Type.String({ format: 'ipv6' })

Validates an IPV6-formatted IP Address.


uri ​
ts
Type.String({ format: 'uri' })

Validates a full URI.


uri-reference ​
ts
Type.String({ format: 'uri-reference' })

uuid ​
ts
Type.String({ format: 'uuid' })

Validates a Universally Unique Identifier according to rfc4122.


uri-template ​
ts
Type.String({ format: 'uri-template' })

Validates a URI Template according to rfc6570.


json-pointer ​
ts
Type.String({ format: 'json-pointer' })

Validates a JSON Pointer, according to RFC6901.


relative-json-pointer ​
ts
Type.String({ format: 'relative-json-pointer' })

Validates a Relative JSON Pointer, according to this draft.


regex ​
ts
Type.String({ format: 'regex' })

Tests whether a string is a valid regular expression by passing it to RegExp constructor.


Additional Formats ​

The ajv-formats package bundled with CLI-generated apps includes additional utilities, listed below, which can be manually enabled by modifying the array of formats in src/schema/validators.ts. The additional formats are highlighted in this code example:

ts
const formats: FormatsPluginOptions = [
  'date-time',
  'time',
  'date',
  'email',
  'hostname',
  'ipv4',
  'ipv6',
  'uri',
  'uri-reference',
  'uuid',
  'uri-template',
  'json-pointer',
  'relative-json-pointer',
  'regex',
  'iso-time',
  'iso-date-time',
  'duration',
  'byte',
  'int32',
  'int64',
  'float',
  'double',
  'password',
  'binary',
]

Be aware that there is also an ajv-formats-draft2019 package which can be manually installed. The package allows use of several international formats for urls, domains, and emails. The formats are included in JSON Schema draft-07.


iso-time ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'iso-time' })

Validates against UTC-based time format:

HH:MM:SS.SSSZ
11:21:44.000Z

HH:MM:SSZ
11:21:44Z

The sections of this format are described as follows:

  • partial-time: HH:MM:SS.SSS (where .SSS represents optional milliseconds)
  • Z: Z (where Z represents UTC time zone, or time offset 00:00)

iso-date-time ​
ts
Type.String({ format: 'iso-date-time' })

Validates against the date-time described in RFC3339/ISO8601, which is the following format:

YYYY-MM-DDTHH:MM:SS.SSSZ
2022-11-30T11:21:44.000Z

YYYY-MM-DDTHH:MM:SSZ
2022-11-30T11:21:44Z

The sections of this format are described as follows:

  • full-date: YYYY-MM-DD
  • partial-time: HH:MM:SS.SSS (where .SSS represents optional milliseconds)
  • Z: Z (where Z represents UTC time zone, or time offset 00:00)

Duration ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'duration' })

A duration string representing a period of time, as specified in rfc3339 appendix-A undder the "Durations" heading. Here's an excerpt of the spec.

Durations:

dur-second        = 1*DIGIT "S"
dur-minute        = 1*DIGIT "M" [dur-second]
dur-hour          = 1*DIGIT "H" [dur-minute]
dur-time          = "T" (dur-hour / dur-minute / dur-second)
dur-day           = 1*DIGIT "D"
dur-week          = 1*DIGIT "W"
dur-month         = 1*DIGIT "M" [dur-day]
dur-year          = 1*DIGIT "Y" [dur-month]
dur-date          = (dur-day / dur-month / dur-year) [dur-time]

duration          = "P" (dur-date / dur-time / dur-week)

Byte ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'byte' })

Validates base64-encoded data according to the openApi 3.0.0 specification.


int32 ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'int32' })

Validates signed (+/-), 32-bit integers according to the openApi 3.0.0 specification.


int64 ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'int64' })

Validates signed (+/-), 64-bit integers according to the openApi 3.0.0 specification.


float ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'float' })

Validates floats according to the openApi 3.0.0 specification.


double ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'double' })

Validates doubles according to the openApi 3.0.0 specification.


password ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'password' })

Validates passwords according to the openApi 3.0.0 specification.


binary ​

Must be manually enabled. See Additional Formats.

ts
Type.String({ format: 'binary' })

Validates a binary string according to the openApi 3.0.0 specification.


Number ​

Creates a number schema and type.

js
const T = Type.Number()
js
type T = number
js
const T = {
  type: 'number'
}
Integer ​

Creates a number schema and type. The number has to be an integer (not a float).

js
const T = Type.Integer()
js
type T = number
js
const T = {
  type: 'integer'
}
Boolean ​

Creates a boolean schema and type.

js
const T = Type.Boolean()
js
type T = boolean
js
const T = {
  type: 'boolean'
}
Null ​

Creates a schema and type only allowing null.

js
const T = Type.Null()
js
type T = null
js
const T = {
  type: 'null'
}
Literal ​

Creates a schema and type that must match the provided value.

js
const T = Type.Literal(42)
js
type T = 42
js
const T = {
  const: 42,
  type: 'number'
}

Object & Array Types ​

These utilities creates schemas and types for objects and arrays.

RegEx ​

Creates a string schema that validates against a regular expression object. The TypeScript type will be string.

js
const T = Type.RegEx(/foo/)
js
type T = string
js
const T = {
  type: 'string',
  pattern: 'foo'
}
Array ​

Creates an array of the provided type. You can use any of the utility types to specify what can go in the array, even complex types using union and intersect.

js
const T = Type.Array(Type.Number())
js
type T = number[]
js
const T = {
  type: 'array',
  items: {
    type: 'number'
  }
}
Object ​

Creates an object schema where all properties are required by default. You can use the Type.Optional utility to mark a key as optional.

js
const T = Type.Object({
  x: Type.Number(),
  y: Type.Number()
})
js
type T = {
  x: number,
  y: number
}
js
const T = {
  type: 'object',
  properties: {
    x: {
      type: 'number'
    },
    y: {
      type: 'number'
    }
  },
  required: ['x', 'y']
}
Tuple ​

Creates an array type with exactly two items matching the specified types.

js
const T = Type.Tuple([Type.Number(), Type.Number()])
js
type T = [number, number]
js
const T = {
  type: 'array',
  items: [{ type: 'number' }, { type: 'number' }],
  additionalItems: false,
  minItems: 2,
  maxItems: 2
}
StringEnum ​

StringEnum is a standalone utility to for specifying an array of allowed string values on a property. It is directly exported from @feathersjs/typebox:

js
// import the module, first
import { StringEnum } from '@feathersjs/typebox'

const T = StringEnum(['crow', 'dove', 'eagle'])
// Add additional options
const T = StringEnum(['crow', 'dove', 'eagle'], {
    default: 'crow'
})

To obtain the TypeScript type, use the Static utility:

js
import { Static } from '@feathersjs/typebox'

type T = Static<typeof T>
js
const T = {
  enum: ['crow', 'dove', 'eagle']
}
Enum ​

tip

For string values, use StringEnum.

js
enum Foo {
  A,
  B,
}
const T = Type.Enum(Foo)
js
enum Foo {
  A,
  B,
}
type T = Foo
js
const T = {
  anyOf: [
    { type: 'number', const: 0 },
    { type: 'number', const: 1 }
  ]
}

Utility Types ​

The utility types create types which are derived from other types.

KeyOf ​

Creates a schema for a string that can be any of the keys of a provided Type.Object. It's similar to TypeScript's KeyOf operator.

js
const T = Type.KeyOf(
  Type.Object({
    x: Type.Number(),
    y: Type.Number()
  })
)
js
type T = keyof {
  x: number,
  y: number,
}
js
const T = {
  anyOf: [
    { type: 'string', const: 'x' },
    { type: 'string', const: 'y' }
  ]
}
Union ​

Creates a type which can be one of the types in the provided array. It's the equivalent to using | to form a TypeScript Union.

js
const T = Type.Union([Type.String(), Type.Number()])
js
type T = string | number
js
const T = {
  anyOf: [{ type: 'string' }, { type: 'number' }]
}
Intersect ​

Creates an object type by combining two or more other object types.

js
const T = Type.Intersect([
  Type.Object({
    x: Type.Number()
  }),
  Type.Object({
    y: Type.Number()
  })
])
js
type T = { x: number } & { y: number }
js
const T = {
  type: 'object',
  properties: {
    x: { type: 'number' },
    y: { type: 'number' }
  },
  required: ['x', 'y']
}
Never ​

Creates a type that will never validate if the attribute is present. This is useful if you are allowing additionalProperties but need to prevent using specific keys.

js
const T = Type.Never()
js
type T = never
js
const T = {
  allOf: [
    { type: 'boolean', const: false },
    { type: 'boolean', const: true }
  ]
}
Record ​

Creates the JSON Schema equivalent of TypeScript's Record utility type.

js
const T = Type.Record(Type.String(), Type.Number())
js
type T = Record<string, number>
js
const T = {
  type: 'object',
  patternProperties: {
    '^.*$': {
      type: 'number'
    }
  }
}
Partial ​

Creates a schema for an object where all keys are optional. It's the opposite of Required, and the JSON Schema equivalent of TypeScript's Partial utility type.

js
const T = Type.Partial(
  Type.Object({
    x: Type.Number(),
    y: Type.Number()
  })
)
js
type T = Partial<{
  x: number,
  y: number
}>
js
const T = {
  type: 'object',
  properties: {
    x: { type: 'number' },
    y: { type: 'number' }
  }
}
Required ​

Creates a schema for an object where all keys are required, even ignoring if keys are marked with Type.Optional. It's the opposite of Partial, and the JSON Schema equivalent of TypeScript's Required utility type.

js
const T = Type.Required(
  Type.Object({
    x: Type.Optional(Type.Number()),
    y: Type.Optional(Type.Number())
  })
)
js
type T = Required<{
  x?: number,
  y?: number
}>
js
const T = {
  type: 'object',
  properties: {
    x: { type: 'number' },
    y: { type: 'number' }
  },
  required: ['x', 'y']
}
Pick ​

Forms a new object containing only the array of keys provided in the second argument. It's the JSON Schema equivalent of TypeScript's Pick utility type.

js
const T = Type.Pick(
  Type.Object({
    x: Type.Number(),
    y: Type.Number()
  }),
  ['x']
)
js
type T = Pick<
  {
    x: number,
    y: number
  },
  'x'
>
js
const T = {
  type: 'object',
  properties: {
    x: { type: 'number' }
  },
  required: ['x']
}
Omit ​

Forms a new object containing all keys except those provided in the second argument. It's the JSON Schema equivalent of TypeScript's Omit utility type.

js
const T = Type.Omit(
  Type.Object({
    x: Type.Number(),
    y: Type.Number()
  }),
  ['x']
)
js
type T = Omit<
  {
    x: number,
    y: number
  },
  'x'
>
js
const T = {
  type: 'object',
  properties: {
    y: { type: 'number' }
  },
  required: ['y']
}

Modifiers ​

TypeBox provides modifiers that can be applied to an objects properties. This allows for optional and readonly to be applied to that property. The following table illustrates how they map between TypeScript and JSON Schema.

Optional ​

Allows marking a key in Type.Object as optional.

js
const T = Type.Object({
  name: Type.Optional(Type.String())
})
js
type T = {
  name?: string
}
js
const T = {
  type: 'object',
  properties: {
    name: {
      type: 'string'
    }
  }
}

Readonly ​

Allows marking a key in Type.Object as readonly. It's the equivalent of TypeScript's Readonly utility type.

js
const T = Type.Object({
  name: Type.Readonly(Type.String())
})
js
type T = {
  readonly name: string
}
js
const T = {
  type: 'object',
  properties: {
    name: {
      type: 'string'
    }
  },
  required: ['name']
}

ReadonlyOptional ​

Allows marking a key in Type.Object as both readonly and optional.

js
const T = Type.Object({
  name: Type.ReadonlyOptional(Type.String())
})
js
type T = {
  readonly name?: string
}
js
const T = {
  type: 'object',
  properties: {
    name: {
      type: 'string'
    }
  }
}

Options by Type ​

You can pass additional JSON schema options on the last argument of any given type. The JSON Schema specification describes options for each data type. Descriptions from the specification are copied here for easy reference.

For Numbers ​

Number types support the following options, which can be used simultaneously.

multipleOf ​

The value of "multipleOf" MUST be a number, strictly greater than 0. Values are valid only if division by this keyword's value results in an integer.

ts
const T = Type.Number({ multipleOf: 2 })
maximum ​

The value of "maximum" MUST be a number, representing an inclusive upper limit for a numeric instance. If the instance is a number, then this keyword validates only if the instance is less than or exactly equal to "maximum".

ts
const T = Type.Number({ maximum: 20 })
exclusiveMaximum ​

The value of "exclusiveMaximum" MUST be a number, representing an exclusive upper limit for a numeric instance. If the instance is a number, then the instance is valid only if it has a value strictly less than (not equal to) "exclusiveMaximum".

ts
const T = Type.Number({ exclusiveMaximum: 20 })
minimum ​

The value of "minimum" MUST be a number, representing an inclusive lower limit for a numeric instance. If the instance is a number, then this keyword validates only if the instance is greater than or exactly equal to "minimum".

ts
const T = Type.Number({ minimum: 20 })
exclusiveMinimum ​

The value of "exclusiveMinimum" MUST be a number, representing an exclusive lower limit for a numeric instance. If the instance is a number, then the instance is valid only if it has a value strictly greater than (not equal to) "exclusiveMinimum".

ts
const T = Type.Number({ exclusiveMinimum: 20 })

For Strings ​

String types support the following options, which can be used simultaneously.

maxLength ​

The value of this keyword MUST be a non-negative integer. A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword. The length of a string instance is defined as the number of its characters as defined by RFC 8259 [RFC8259].

minLength ​

The value of this keyword MUST be a non-negative integer. A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword. The length of a string instance is defined as the number of its characters as defined by RFC 8259 [RFC8259]. Omitting this keyword has the same behavior as a value of 0.

pattern ​

Use Type.Regex, instead of this option.

With AJV Formats ​

There are four custom options which are only available for certain formats when using the ajv-formats package:

The above-listed options are only available when using the following string formats.

formatMinimum ​

Allows defining minimum constraints when the format keyword defines ordering (using the compare function in format definition). Available when using ajv-formats.

The following example validates that the provided date is on or after November 13, 2022.

ts
Type.String({ format: 'date', formatMinimum: '2022-11-13' })
formatMaximum ​

Allows defining maximum constraints when the format keyword defines ordering (using the compare function in format definition). Available when using ajv-formats.

The following example validates that the provided date is on or before November 13, 2022.

ts
Type.String({ format: 'date', formatMaximum: '2022-11-13' })
formatExclusiveMinimum ​

Allows defining exclusive minimum constraints when the format keyword defines ordering (using the compare function in format definition). Available when using ajv-formats.

The following example validates that the provided date is after (and not on) November 13, 2022.

ts
Type.String({ format: 'date', formatExclusiveMinimum: '2022-11-13' })
formatExclusiveMaximum ​

Allows defining exclusive maximum constraints when the format keyword defines ordering (using the compare function in format definition). Available when using ajv-formats.

The following example validates that the provided date is before (and not on) November 13, 2022.

ts
Type.String({ format: 'date', formatExclusiveMaximum: '2022-11-13' })

For Arrays ​

Array types support the following options, which can be used simultaneously.

maxItems ​

The value of this keyword MUST be a non-negative integer. An array instance is valid against "maxItems" if its size is less than, or equal to, the value of this keyword.

minItems ​

The value of this keyword MUST be a non-negative integer. An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword. Omitting this keyword has the same behavior as a value of 0.

uniqueItems ​

The value of this keyword MUST be a boolean. If this keyword has boolean value false, the instance validates successfully. If it has boolean value true, the instance validates successfully if all of its elements are unique. Omitting this keyword has the same behavior as a value of false.

maxContains ​

The value of this keyword MUST be a non-negative integer.

If "contains" is not present within the same schema object, then this keyword has no effect.

An instance array is valid against "maxContains" in two ways, depending on the form of the annotation result of an adjacent "contains" [json-schema] keyword. The first way is if the annotation result is an array and the length of that array is less than or equal to the "maxContains" value. The second way is if the annotation result is a boolean "true" and the instance array length is less than or equal to the "maxContains" value.

minContains ​

The value of this keyword MUST be a non-negative integer.

If "contains" is not present within the same schema object, then this keyword has no effect.

An instance array is valid against "minContains" in two ways, depending on the form of the annotation result of an adjacent "contains" [json-schema] keyword. The first way is if the annotation result is an array and the length of that array is greater than or equal to the "minContains" value. The second way is if the annotation result is a boolean "true" and the instance array length is greater than or equal to the "minContains" value.

A value of 0 is allowed, but is only useful for setting a range of occurrences from 0 to the value of "maxContains". A value of 0 causes "minContains" and "contains" to always pass validation (but validation can still fail against a "maxContains" keyword).

Omitting this keyword has the same behavior as a value of 1.

For Objects ​

Array types support the following options, which can be used simultaneously.

additionalProperties ​

Specifies if keys other than the ones specified in the schema are allowed to be present in the object.

maxProperties ​

The value of this keyword MUST be a non-negative integer. An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.

minProperties ​

The value of this keyword MUST be a non-negative integer. An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword. Omitting this keyword has the same behavior as a value of 0.

required ​

All TypeBox types are required unless you wrap them in Type.Optional, so you don't need to use this option, manually.

dependentRequired ​

The value of this keyword MUST be an object. Properties in this object, if any, MUST be arrays. Elements in each array, if any, MUST be strings, and MUST be unique.

This keyword specifies properties that are required if a specific other property is present. Their requirement is dependent on the presence of the other property.

Validation succeeds if, for each name that appears in both the instance and as a name within this keyword's value, every item in the corresponding array is also the name of a property in the instance.

Omitting this keyword has the same behavior as an empty object.

Extended ​

In addition to JSON schema types, TypeBox provides several extended types that allow for the composition of function and constructor types. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. Since these are nonstandard types, most applications will not need them. Consider using the Standard Types, instead, as using these types may make it difficult to upgrade your application in the future.

Extended Configuration ​

Utilities in this section require updating src/schemas/validators.ts to the Extended Ajv Configuration, as shown here:

ts
import { TypeGuard } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'
import addFormats from 'ajv-formats'
import type { Options } from 'ajv'
import Ajv from 'ajv'

function schemaOf(schemaOf: string, value: unknown, schema: unknown) {
  switch (schemaOf) {
    case 'Constructor':
      return TypeGuard.IsConstructor(schema) && Value.Check(schema, value) // not supported
    case 'Function':
      return TypeGuard.IsFunction(schema) && Value.Check(schema, value) // not supported
    case 'Date':
      return TypeGuard.IsDate(schema) && Value.Check(schema, value)
    case 'Promise':
      return TypeGuard.IsPromise(schema) && Value.Check(schema, value) // not supported
    case 'Uint8Array':
      return TypeGuard.IsUint8Array(schema) && Value.Check(schema, value)
    case 'Undefined':
      return TypeGuard.IsUndefined(schema) && Value.Check(schema, value) // not supported
    case 'Void':
      return TypeGuard.IsVoid(schema) && Value.Check(schema, value)
    default:
      return false
  }
}

export function createAjv(options: Options = {}) {
  return addFormats(new Ajv(options), [
    'date-time',
    'time',
    'date',
    'email',
    'hostname',
    'ipv4',
    'ipv6',
    'uri',
    'uri-reference',
    'uuid',
    'uri-template',
    'json-pointer',
    'relative-json-pointer',
    'regex',
  ])
  .addKeyword({ type: 'object', keyword: 'instanceOf', validate: schemaOf })
  .addKeyword({ type: 'null', keyword: 'typeOf', validate: schemaOf })
  .addKeyword('exclusiveMinimumTimestamp')
  .addKeyword('exclusiveMaximumTimestamp')
  .addKeyword('minimumTimestamp')
  .addKeyword('maximumTimestamp')
  .addKeyword('minByteLength')
  .addKeyword('maxByteLength')
}

export const dataValidator: Ajv = createAjv({})
export const queryValidator: Ajv = createAjv({ coerceTypes: true })

If you see an error stating Error: strict mode: unknown keyword: "instanceOf", it's likely because you need to extend your configuration, as shown above.

Constructor ​

Verifies that the value is a constructor with typed arguments and return value. Requires Extended Ajv Configuration.

js
const T = Type.Constructor([Type.String(), Type.Number()], Type.Boolean())
js
type T = new (
  arg0: string,
  arg1: number,
) => boolean
js
const T = {
  type: 'constructor',
  parameters: [
    { type: 'string' },
    { type: 'number' },
  ],
  return {
    type: 'boolean',
  },
}

Function ​

Verifies that the value is a function with typed arguments and return value. Requires Extended Ajv Configuration.

js
const T = Type.Function([Type.String(), Type.Number()], Type.Boolean())
js
type T = ({
  arg0: string,
  arg1: number
}) => boolean
js
const T = {
  type: 'function',
  parameters: [
    { type: 'string' },
    { type: 'number' },
  ],
  return {
    type: 'boolean',
  },
}

Promise ​

Verifies that the value is an instanceof Promise which resolves to the provided type. Requires Extended Ajv Configuration.

js
const T = Type.Promise(Type.String())
js
type T = Promise<string>
js
const T = {
  type: 'promise',
  item: { type: 'string' }
}

Uint8Array ​

Verifies that the value is an instanceof Uint8Array. Requires Extended Ajv Configuration.

js
const T = Type.Uint8Array()
js
type T = Uint8Array
js
const T = {
  type: 'object',
  instanceOf: 'Uint8Array'
}

Date ​

Verifies that the value is an instanceof Date. This is likely not the validator to use for storing dates in a database. See Validating Dates. Requires Extended Ajv Configuration.

js
const T = Type.Date()
js
type T = Date
js
const T = {
  type: 'object',
  instanceOf: 'Date'
}

Undefined ​

Verifies that the value is undefined. Requires Extended Ajv Configuration.

js
const T = Type.Undefined()
js
type T = undefined
js
const T = {
  type: 'object',
  specialized: 'Undefined'
}

Symbol ​

Verifies that the value is of type Symbol. Requires Extended Ajv Configuration.

js
const T = Type.Symbol()
js
type T = symbol
js
const T = {
  type: 'null',
  typeOf: 'Symbol'
}

BigInt ​

Verifies that the value is of type BigInt. Requires Extended Ajv Configuration.

js
const T = Type.BigInt()
js
type T = bigint
js
const T = {
  type: 'null',
  typeOf: 'BigInt'
}

Void ​

Verifies that the value is null. Requires Extended Ajv Configuration.

js
const T = Type.Void()
js
type T = void
js
const T = {
  type: 'null'
}

Reference ​

Use Type.Ref(...) to create referenced types. The target type must specify an $id.

ts
const T = Type.String({ $id: 'T' })
const R = Type.Ref(T)

For a more detailed example see the result and data schema section.

Released under the MIT License.