タイププロバイダー
タイププロバイダー
タイププロバイダーは、TypeScript専用機能であり、FastifyがインラインJSONスキーマから静的に型情報を推論することを可能にします。これは、ルートにジェネリック引数を指定する代わりに使用でき、プロジェクトで定義された各スキーマに関連する型を維持する必要性を大幅に削減できます。
プロバイダー
タイププロバイダーは、プロジェクトにインストールする必要がある追加パッケージとして提供されます。各プロバイダーは内部で異なる推論ライブラリを使用しており、ニーズに最も適したライブラリを選択できます。公式のタイププロバイダーパッケージは@fastify/type-provider-{provider-name}
という命名規則に従い、いくつかのコミュニティパッケージも利用可能です。
次の推論パッケージがサポートされています。
それぞれのパッケージのタイププロバイダーラッパーパッケージも参照してください。
@fastify/type-provider-json-schema-to-ts
@fastify/type-provider-typebox
fastify-type-provider-zod
(サードパーティ)
Json Schema to Ts
以下は、json-schema-to-ts
タイププロバイダーの設定方法です。
$ npm i @fastify/type-provider-json-schema-to-ts
import fastify from 'fastify'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
const server = fastify().withTypeProvider<JsonSchemaToTsProvider>()
server.get('/route', {
schema: {
querystring: {
type: 'object',
properties: {
foo: { type: 'number' },
bar: { type: 'string' },
},
required: ['foo', 'bar']
}
}
}, (request, reply) => {
// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})
TypeBox
以下は、TypeBoxタイププロバイダーの設定方法です。
$ npm i @fastify/type-provider-typebox
import fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'
const server = fastify().withTypeProvider<TypeBoxTypeProvider>()
server.get('/route', {
schema: {
querystring: Type.Object({
foo: Type.Number(),
bar: Type.String()
})
}
}, (request, reply) => {
// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})
TypeBoxとAJVを連携させる方法については、TypeBoxドキュメントも参照してください。
Zod
Zodタイププロバイダーの指示については、公式ドキュメントを参照してください。
スコープ付きタイププロバイダー
プロバイダの型はグローバルには伝播しません。カプセル化された使用方法では、コンテキストを再マッピングして1つ以上のプロバイダー(例:typebox
とjson-schema-to-ts
)を同じアプリケーションで使用できます。
例
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
import { Type } from '@sinclair/typebox'
const fastify = Fastify()
function pluginWithTypebox(fastify: FastifyInstance, _opts, done): void {
fastify.withTypeProvider<TypeBoxTypeProvider>()
.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
const { x, y, z } = req.body // type safe
});
done()
}
function pluginWithJsonSchema(fastify: FastifyInstance, _opts, done): void {
fastify.withTypeProvider<JsonSchemaToTsProvider>()
.get('/', {
schema: {
body: {
type: 'object',
properties: {
x: { type: 'string' },
y: { type: 'number' },
z: { type: 'boolean' }
},
}
}
}, (req) => {
const { x, y, z } = req.body // type safe
});
done()
}
fastify.register(pluginWithJsonSchema)
fastify.register(pluginWithTypebox)
また、型はグローバルに伝播しないため、現在複数のスコープを扱う場合、ルートに複数の登録を避けることは不可能であることに注意することが重要です。以下を参照してください。
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'
const server = Fastify().withTypeProvider<TypeBoxTypeProvider>()
server.register(plugin1) // wrong
server.register(plugin2) // correct
function plugin1(fastify: FastifyInstance, _opts, done): void {
fastify.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// it doesn't work! in a new scope needs to call `withTypeProvider` again
const { x, y, z } = req.body
});
done()
}
function plugin2(fastify: FastifyInstance, _opts, done): void {
const server = fastify.withTypeProvider<TypeBoxTypeProvider>()
server.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// works
const { x, y, z } = req.body
});
done()
}
FastifyInstance + タイププロバイダーの型定義
モジュールを使用する場合、タイププロバイダーのジェネリックを伴うFastifyInstance
を使用する必要があります。以下の例を参照してください。
// index.ts
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { registerRoutes } from './routes'
const server = Fastify().withTypeProvider<TypeBoxTypeProvider>()
registerRoutes(server)
server.listen({ port: 3000 })
// routes.ts
import { Type } from '@sinclair/typebox'
import {
FastifyInstance,
FastifyBaseLogger,
RawReplyDefaultExpression,
RawRequestDefaultExpression,
RawServerDefault
} from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
type FastifyTypebox = FastifyInstance<
RawServerDefault,
RawRequestDefaultExpression<RawServerDefault>,
RawReplyDefaultExpression<RawServerDefault>,
FastifyBaseLogger,
TypeBoxTypeProvider
>;
export function registerRoutes(fastify: FastifyTypebox): void {
fastify.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// works
const { x, y, z } = req.body
});
}