* feat(docs) new docs site started * Minor page edits * feat(footer) fix spacing * empty guides template Co-authored-by: daffl <daff@neyeon.com>
6.6 KiB
Schemas
schema is a small wrapper over JSON schema, AJV and json-schema-to-ts to define data schemas. It can also automatically get the correct TypeScript type for a schema. This 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.
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:
:::: tabs :options="{ useUrlFragment: false }"
::: tab "JavaScript"
import { schema } 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' }
}
});
:::
::: tab "TypeScript"
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);
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:
:::: tabs :options="{ useUrlFragment: false }"
::: tab "JavaScript"
import { schema } 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' }
}
});
// The user result has all properties from the user but also an
// additional `id` added by the database
export const userResultSchema = schema({
$id: 'UserResult',
type: 'object',
additionalProperties: false,
required: [...userSchema.required, 'id'],
properties: {
...userSchema.properties,
id: { type: 'number' }
}
});
:::
::: tab "TypeScript"
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.
:::: tabs :options="{ useUrlFragment: false }"
::: tab "JavaScript"
import { schema } 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 const messageSchema = schema({
$id: 'Message',
type: 'object',
additionalProperties: false,
required: ['text'],
properties: {
text: { type: 'string' },
user: { $ref: 'User' }
}
});
:::
::: tab "TypeScript" 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
}
:::
::::
Query helper
Schema ships with the following helpers to automatically create schemas that follow the Feathers query syntax (like $gt, $ne etc.):
queryPropertyhelper takes a definition for a single property (usually{ type: '<type>' }) and returns a schema that allows the default query operatorsqueryProperties(schema.properties)takes the all properties of a schema and converts them into query schema properties (usingqueryProperty)querySyntax(schema.properties)initializes all properties the additional query syntax properties$limit,$skip,$selectand$sort.$selectand$sortwill 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
}
}
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.