Skip to content
On this page

Schemas

schema is a small wrapper around three existing projects:

This package essentially allows for a single place to define your types and validation rules in plain JavaScript or TypeScript which can then be used by many other parts of a Feathers application.

Schemas are also used by resolvers to validate and convert data before or after dynamically resolving properties.

Need JSON Schema help?

You can find a lot of type-specific JSON Schema examples in the json-schema-to-ts docs.

Definitions

If you are not familiar with JSON schema have a look at the official getting started guide. Here is an example for a possible user schema:

import { HookContext } from './definitions'
import { schema, Infer } from '@feathersjs/schema'

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
} as const)

export type User = Infer<typeof userSchema>
import { HookContext } from './definitions'
import { schema, Infer } from '@feathersjs/schema'

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
} as const)

export type User = Infer<typeof userSchema>

Very Important: To get the correct TypeScript types the definition always needs to be declared via schema({} as const).

Options

schema(definition, ajv) allows to initialize a schema with a custom AJV instance:

import ajvErrors from 'ajv-errors';
import Ajv form 'ajv';
import { schema } from '@feathersjs/schema';

const ajv = new Ajv({
  coerceTypes: true
});

ajvErrors(ajv);

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
}, ajv);

import ajvErrors from 'ajv-errors';
import Ajv form 'ajv';
import { schema } from '@feathersjs/schema';

const ajv = new Ajv({
  coerceTypes: true
});

ajvErrors(ajv);

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
}, ajv);

Extension

To create a new schema that extends an existing one, combine the schema properties from schema.properties (an schema.required if needed) with the new properties:

import { HookContext } from './definitions'
import { schema, Infer } from '@feathersjs/schema'

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    email: { type: 'string' },
    password: { type: 'string' }
  }
} as const)

export type User = Infer<typeof userSchema>

export const userResultSchema = schema({
  $id: 'UserResult',
  type: 'object',
  additionalProperties: false,
  required: [...userSchema.required, 'id'],
  properties: {
    ...userSchema.properties,
    id: { type: 'number' }
  }
})

export type User = Infer<typeof userResultSchema>
import { HookContext } from './definitions'
import { schema, Infer } from '@feathersjs/schema'

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    email: { type: 'string' },
    password: { type: 'string' }
  }
} as const)

export type User = Infer<typeof userSchema>

export const userResultSchema = schema({
  $id: 'UserResult',
  type: 'object',
  additionalProperties: false,
  required: [...userSchema.required, 'id'],
  properties: {
    ...userSchema.properties,
    id: { type: 'number' }
  }
})

export type User = Infer<typeof userResultSchema>

Associations

Associated schemas can be initialized via the $ref keyword referencing the $id set during schema definition.

In TypeScript the referenced type needs to be added explicitly.

import { HookContext } from './definitions'
import { schema, Infer } from '@feathersjs/schema'

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
})

export type User = Infer<typeof userSchema>

export const messageSchema = schema({
  $id: 'Message',
  type: 'object',
  additionalProperties: false,
  required: ['text'],
  properties: {
    text: { type: 'string' },
    user: { $ref: 'User' }
  }
})

export type Message = Infer<typeof messageSchema> & {
  user: User
}
import { HookContext } from './definitions'
import { schema, Infer } from '@feathersjs/schema'

export const userSchema = schema({
  $id: 'User',
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
})

export type User = Infer<typeof userSchema>

export const messageSchema = schema({
  $id: 'Message',
  type: 'object',
  additionalProperties: false,
  required: ['text'],
  properties: {
    text: { type: 'string' },
    user: { $ref: 'User' }
  }
})

export type Message = Infer<typeof messageSchema> & {
  user: User
}

Query helper

Schema ships with the following helpers to automatically create schemas that follow the Feathers query syntax (like $gt, $ne etc.):

  • queryProperty helper takes a definition for a single property (usually { type: '<type>' }) and returns a schema that allows the default query operators
  • queryProperties(schema.properties) takes the all properties of a schema and converts them into query schema properties (using queryProperty)
  • querySyntax(schema.properties) initializes all properties the additional query syntax properties $limit, $skip, $select and $sort. $select and $sort will be typed so they only allow existing schema properties.
import { querySyntax } from '@feathersjs/schema';

export const userQuerySchema = schema({
  $id: 'UserQuery',
  type: 'object',
  additionalProperties: false,
  properties: {
    ...querySyntax(userSchema.properties)
  }
} as const);

export type UserQuery = Infer<typeof userQuerySchema>

const userQuery: UserQuery = {
  $limit: 10,
  $select: [ 'email', 'id' ],
  $sort: {
    email: 1
  }
}
import { querySyntax } from '@feathersjs/schema';

export const userQuerySchema = schema({
  $id: 'UserQuery',
  type: 'object',
  additionalProperties: false,
  properties: {
    ...querySyntax(userSchema.properties)
  }
} as const);

export type UserQuery = Infer<typeof userQuerySchema>

const userQuery: UserQuery = {
  $limit: 10,
  $select: [ 'email', 'id' ],
  $sort: {
    email: 1
  }
}

Validation hooks

Schemas will be used for validation when they are passed to a Resolver. See the Feathers resolver on how to use the schema with resolvers.

Released under the MIT License.