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:
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
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.
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:
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:
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
.
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.
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:
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.
const T = Type.Any()
type T = any
const T = {}
Unknown
Similar to any, it creates a schema that will always pass validation. It's the equivalent of TypeScript's unknown type.
const T = Type.Unknown()
type T = unknown
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.
const T = Type.String()
type T = string
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
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
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
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
Type.String({ format: 'email' })
Validates email addresses against the format specified by RFC 1034.
hostname
Type.String({ format: 'hostname' })
Validates hostnames against the format specified by RFC 1034.
ipv4
Type.String({ format: 'ipv4' })
Validates an IPV4-formatted IP Address.
0.0.0.0 to 255.255.255.255
ipv6
Type.String({ format: 'ipv6' })
Validates an IPV6-formatted IP Address.
uri
Type.String({ format: 'uri' })
Validates a full URI.
uri-reference
Type.String({ format: 'uri-reference' })
uuid
Type.String({ format: 'uuid' })
Validates a Universally Unique Identifier according to rfc4122.
uri-template
Type.String({ format: 'uri-template' })
Validates a URI Template according to rfc6570.
json-pointer
Type.String({ format: 'json-pointer' })
Validates a JSON Pointer, according to RFC6901.
relative-json-pointer
Type.String({ format: 'relative-json-pointer' })
Validates a Relative JSON Pointer, according to this draft.
regex
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:
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.
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
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.
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.
Type.String({ format: 'byte' })
Validates base64-encoded data according to the openApi 3.0.0 specification.
int32
Must be manually enabled. See Additional Formats.
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.
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.
Type.String({ format: 'float' })
Validates floats according to the openApi 3.0.0 specification.
double
Must be manually enabled. See Additional Formats.
Type.String({ format: 'double' })
Validates doubles according to the openApi 3.0.0 specification.
password
Must be manually enabled. See Additional Formats.
Type.String({ format: 'password' })
Validates passwords according to the openApi 3.0.0 specification.
binary
Must be manually enabled. See Additional Formats.
Type.String({ format: 'binary' })
Validates a binary string according to the openApi 3.0.0 specification.
Number
Creates a number schema and type.
const T = Type.Number()
type T = number
const T = {
type: 'number'
}
Integer
Creates a number schema and type. The number has to be an integer (not a float).
const T = Type.Integer()
type T = number
const T = {
type: 'integer'
}
Boolean
Creates a boolean schema and type.
const T = Type.Boolean()
type T = boolean
const T = {
type: 'boolean'
}
Null
Creates a schema and type only allowing null
.
const T = Type.Null()
type T = null
const T = {
type: 'null'
}
Literal
Creates a schema and type that must match the provided value.
const T = Type.Literal(42)
type T = 42
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
.
const T = Type.RegEx(/foo/)
type T = string
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.
const T = Type.Array(Type.Number())
type T = number[]
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.
const T = Type.Object({
x: Type.Number(),
y: Type.Number()
})
type T = {
x: number,
y: number
}
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.
const T = Type.Tuple([Type.Number(), Type.Number()])
type T = [number, number]
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
:
// 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:
import { Static } from '@feathersjs/typebox'
type T = Static<typeof T>
const T = {
enum: ['crow', 'dove', 'eagle']
}
Enum
tip
For string values, use StringEnum.
enum Foo {
A,
B,
}
const T = Type.Enum(Foo)
enum Foo {
A,
B,
}
type T = Foo
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.
const T = Type.KeyOf(
Type.Object({
x: Type.Number(),
y: Type.Number()
})
)
type T = keyof {
x: number,
y: number,
}
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.
const T = Type.Union([Type.String(), Type.Number()])
type T = string | number
const T = {
anyOf: [{ type: 'string' }, { type: 'number' }]
}
Intersect
Creates an object type by combining two or more other object types.
const T = Type.Intersect([
Type.Object({
x: Type.Number()
}),
Type.Object({
y: Type.Number()
})
])
type T = { x: number } & { y: number }
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.
const T = Type.Never()
type T = never
const T = {
allOf: [
{ type: 'boolean', const: false },
{ type: 'boolean', const: true }
]
}
Record
Creates the JSON Schema equivalent of TypeScript's Record utility type.
const T = Type.Record(Type.String(), Type.Number())
type T = Record<string, number>
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.
const T = Type.Partial(
Type.Object({
x: Type.Number(),
y: Type.Number()
})
)
type T = Partial<{
x: number,
y: number
}>
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.
const T = Type.Required(
Type.Object({
x: Type.Optional(Type.Number()),
y: Type.Optional(Type.Number())
})
)
type T = Required<{
x?: number,
y?: number
}>
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.
const T = Type.Pick(
Type.Object({
x: Type.Number(),
y: Type.Number()
}),
['x']
)
type T = Pick<
{
x: number,
y: number
},
'x'
>
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.
const T = Type.Omit(
Type.Object({
x: Type.Number(),
y: Type.Number()
}),
['x']
)
type T = Omit<
{
x: number,
y: number
},
'x'
>
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.
const T = Type.Object({
name: Type.Optional(Type.String())
})
type T = {
name?: string
}
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.
const T = Type.Object({
name: Type.Readonly(Type.String())
})
type T = {
readonly name: string
}
const T = {
type: 'object',
properties: {
name: {
type: 'string'
}
},
required: ['name']
}
ReadonlyOptional
Allows marking a key in Type.Object as both readonly and optional.
const T = Type.Object({
name: Type.ReadonlyOptional(Type.String())
})
type T = {
readonly name?: string
}
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.
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".
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".
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".
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".
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.
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.
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.
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.
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:
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.
const T = Type.Constructor([Type.String(), Type.Number()], Type.Boolean())
type T = new (
arg0: string,
arg1: number,
) => boolean
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.
const T = Type.Function([Type.String(), Type.Number()], Type.Boolean())
type T = ({
arg0: string,
arg1: number
}) => boolean
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.
const T = Type.Promise(Type.String())
type T = Promise<string>
const T = {
type: 'promise',
item: { type: 'string' }
}
Uint8Array
Verifies that the value is an instanceof Uint8Array. Requires Extended Ajv Configuration.
const T = Type.Uint8Array()
type T = Uint8Array
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.
const T = Type.Date()
type T = Date
const T = {
type: 'object',
instanceOf: 'Date'
}
Undefined
Verifies that the value is undefined
. Requires Extended Ajv Configuration.
const T = Type.Undefined()
type T = undefined
const T = {
type: 'object',
specialized: 'Undefined'
}
Symbol
Verifies that the value is of type Symbol
. Requires Extended Ajv Configuration.
const T = Type.Symbol()
type T = symbol
const T = {
type: 'null',
typeOf: 'Symbol'
}
BigInt
Verifies that the value is of type BigInt
. Requires Extended Ajv Configuration.
const T = Type.BigInt()
type T = bigint
const T = {
type: 'null',
typeOf: 'BigInt'
}
Void
Verifies that the value is null
. Requires Extended Ajv Configuration.
const T = Type.Void()
type T = void
const T = {
type: 'null'
}
Reference
Use Type.Ref(...)
to create referenced types. The target type must specify an $id
.
const T = Type.String({ $id: 'T' })
const R = Type.Ref(T)
For a more detailed example see the result and data schema section.