Fastify

TypeScript

Fastify 是用普通的 JavaScript 编写的,因此,类型定义的维护并不容易。可喜的是,自版本 2 以来,维护者和贡献者们已经在类型维护上投入了巨大的努力。

版本 3 的类型系统发生了改变。新的系统带来了泛型约束 (generic constraining) 与默认值,以及定义请求 body,querystring 等 schema 的新方式!在团队改善框架和类型定义的协作中,难免有所纰漏。我们鼓励你参与贡献。请记得在开始前阅读 CONTRIBUTING.md 一文!

本文档介绍的是 Fastify 3.x 版本的类型

插件不一定包含类型定义。更多内容请看插件。我们鼓励用户提交 PR 来改善插件的类型支持。

别忘了安装 @types/node

从例子中学习

通过例子来学习 Fastify 的类型系统是最好的途径!以下四个例子涵盖了最常见的开发场景。例子之后是更详尽深入的文档。

起步

这个例子展示了如何使用 Fastify 和 TypeScript 构建一个最简单的 http 服务器。

  1. 创建一个 npm 项目,安装 Fastify、typescript 和 node.js 的类型文件:
  1. npm init -y
  2. npm i fastify
  3. npm i -D typescript @types/node
  1. package.json"scripts" 里添加以下内容:
  1. {
  2. "scripts": {
  3. "build": "tsc -p tsconfig.json",
  4. "start": "node index.js"
  5. }
  6. }
  1. 初始化 TypeScript 配置文件:
  1. npx tsc --init

或使用一个推荐的配置文件。 4. 创建 index.ts ,在此编写服务器的代码。 5. 将下列代码添加到该文件中:

  1. import fastify from 'fastify'
  2. const server = fastify()
  3. server.get('/ping', async (request, reply) => {
  4. return 'pong\n'
  5. })
  6. server.listen(8080, (err, address) => {
  7. if (err) {
  8. console.error(err)
  9. process.exit(1)
  10. }
  11. console.log(`Server listening at ${address}`)
  12. })
  1. 执行 npm run build。这么做会将 index.ts 编译为能被 Node.js 运行的 index.js。如果遇到了错误,请在 fastify/help 发布 issue。
  2. 执行 npm run start 来启动 Fastify 服务器。
  3. 你将看到控制台输出: Server listening at http://127.0.0.1:8080
  4. 通过 curl localhost:8080/ping 访问服务,你将收到 pong

🎉 现在,你有了一个能用的 TypeScript 写的 Fastify 服务器!这个例子演示了在 3.x 版本中,类型系统有多么简单。默认情况下,类型系统会假定你使用的是 http 服务器。后续的例子将为你展现更多内容,例如,创建较为复杂的服务器 (httpshttp2),以及指定路由的 schema!

更多使用 TypeScript 初始化 Fastify 的示例 (如启用 HTTP2),请在这里查阅详细的 API。

使用泛型

类型系统重度依赖于泛型属性来提供最精确的开发时体验。有人可能会认为这么做有些麻烦,但这是值得的!这个例子将展示如何在路由 schema 中实现泛型,以及路由层 request 对象上的动态属性。

  1. 照着上面例子的 1-4 步来初始化项目。

  2. index.ts 中定义两个接口 (interface),IQuerystringIHeaders

    1. interface IQuerystring {
    2. username: string;
    3. password: string;
    4. }
    5. interface IHeaders {
    6. 'h-Custom': string;
    7. }
  3. 使用这两个接口,定义一个新的 API 路由,并将它们用作泛型。路由方法的简写形式 (如 .get) 接受一个泛型对象 RequestGenericInterface,它包含了四个具名属性:BodyQuerystringParams 以及 Headers。接口会随着路由方法向下传递,到达路由处理函数中的 request 实例。

    1. server.get<{
    2. Querystring: IQuerystring,
    3. Headers: IHeaders
    4. }>('/auth', async (request, reply) => {
    5. const { username, password } = request.query
    6. const customerHeader = request.headers['h-Custom']
    7. // 处理请求数据
    8. return `logged in!`
    9. })
  4. 执行 npm run buildnpm run start 来构建并运行项目。

  5. 访问 api:

    1. curl localhost:8080/auth?username=admin&password=Password123!

    将会返回 logged in!

  6. 此外,泛型接口还可以用在路由层钩子方法中。在上面的路由内加上一个 preValidation 钩子:

    1. server.get<{
    2. Querystring: IQuerystring,
    3. Headers: IHeaders
    4. }>('/auth', {
    5. preValidation: (request, reply, done) => {
    6. const { username, password } = request.query
    7. done(username !== 'admin' ? new Error('Must be admin') : undefined) // only validate `admin` account
    8. }
    9. }, async (request, reply) => {
    10. const customerHeader = request.headers['h-Custom']
    11. // 处理请求数据
    12. return `logged in!`
    13. })
  7. 构建运行之后,使用任何值不为 adminusername 查询字符串访问服务。你将收到一个 500 错误:{"statusCode":500,"error":"Internal Server Error","message":"Must be admin"}

    干得漂亮。现在你能够为每个路由定义接口,并拥有严格类型的请求与响应实例了。Fastify 类型系统的其他部分依赖于泛型属性。关于如何使用它们,请参照后文详细的类型系统文档。

JSON Schema

你可以通过 JSON Schema 来验证请求与响应。给 Fastify 路由定义 schema 还能提高吞吐量!更多信息请见验证和序列化

此外,在路由处理函数 (包括 pre-validation 等钩子) 中使用定义好的类型也是有好处的。

以下列出了几种实现方案。

typebox

typebox 能帮助你同时构建类型与 schema。通过 typebox 在代码里定义好 schema 之后,你便能将其当作类型或 schema 来使用。

在 Fastify 路由中验证 payload,你可以这么做:

  1. 安装 typebox

    1. npm i @sinclair/typebox
  2. 使用 Type 定义 schema,并通过 Static 创建相应的类型。

    1. import { Static, Type } from '@sinclair/typebox'
    2. const User = Type.Object({
    3. name: Type.String(),
    4. mail: Type.Optional(Type.String({ format: "email" })),
    5. });
    6. type UserType = Static<typeof User>;
  3. 在路由中使用定义好的类型与 schema。

    1. const app = fastify();
    2. app.post<{ Body: UserType; Response: UserType }>(
    3. "/",
    4. {
    5. schema: {
    6. body: User,
    7. response: {
    8. 200: User,
    9. },
    10. },
    11. },
    12. (req, rep) => {
    13. const { body: user } = req;
    14. /* user 的类型如下:
    15. * const user: StaticProperties<{
    16. * name: TString;
    17. * mail: TOptional<TString>;
    18. * }>
    19. */
    20. //...
    21. rep.status(200).send(user);
    22. }
    23. );

Schemas in JSON Files

在上一个例子里,我们使用接口定义了请求 querystring 和 header 的类型。许多用户使用 JSON Schema 来处理这些工作,幸运的是,有一套方法能将现有的 JSON Schema 转换为 TypeScript 接口!

  1. 完成 ‘起步’ 中例子的 1-4 步。

  2. 安装 json-schema-to-typescript 模块:

    1. npm i -D json-schema-to-typescript
  3. 新建一个名为 schemas 的文件夹。在其中添加 headers.jsonquerystring.json 两个文件,将下面的 schema 定义粘贴到对应文件中。

    1. {
    2. "title": "Headers Schema",
    3. "type": "object",
    4. "properties": {
    5. "h-Custom": { "type": "string" }
    6. },
    7. "additionalProperties": false,
    8. "required": ["h-Custom"]
    9. }
    1. {
    2. "title": "Querystring Schema",
    3. "type": "object",
    4. "properties": {
    5. "username": { "type": "string" },
    6. "password": { "type": "string" }
    7. },
    8. "additionalProperties": false,
    9. "required": ["username", "password"]
    10. }
  4. 在 package.json 里加上一行 compile-schemas 脚本:

    1. {
    2. "scripts": {
    3. "compile-schemas": "json2ts -i schemas -o types"
    4. }
    5. }

    json2ts 是囊括在 json-schema-to-typescript 中的命令行工具。schemas 是输入路径,types 则是输出路径。

  5. 执行 npm run compile-schemas,在 types 文件夹下生成两个新文件。

  6. 更新 index.ts

    1. import fastify from 'fastify'
    2. // 导入 json schema
    3. import QuerystringSchema from './schemas/querystring.json'
    4. import HeadersSchema from './schemas/headers.json'
    5. // 导入生成的接口
    6. import { QuerystringSchema as QuerystringSchemaInterface } from './types/querystring'
    7. import { HeadersSchema as HeadersSchemaInterface } from './types/headers'
    8. const server = fastify()
    9. server.get<{
    10. Querystring: QuerystringSchemaInterface,
    11. Headers: HeadersSchemaInterface
    12. }>('/auth', {
    13. schema: {
    14. querystring: QuerystringSchema,
    15. headers: HeadersSchema
    16. },
    17. preValidation: (request, reply, done) => {
    18. const { username, password } = request.query
    19. done(username !== 'admin' ? new Error('Must be admin') : undefined)
    20. }
    21. }, async (request, reply) => {
    22. const customerHeader = request.headers['h-Custom']
    23. // 处理请求数据
    24. return `logged in!`
    25. })
    26. server.route<{
    27. Querystring: QuerystringSchemaInterface,
    28. Headers: HeadersSchemaInterface
    29. }>({
    30. method: 'GET',
    31. url: '/auth2',
    32. schema: {
    33. querystring: QuerystringSchema,
    34. headers: HeadersSchema
    35. },
    36. preHandler: (request, reply) => {
    37. const { username, password } = request.query
    38. const customerHeader = request.headers['h-Custom']
    39. },
    40. handler: (request, reply) => {
    41. const { username, password } = request.query
    42. const customerHeader = request.headers['h-Custom']
    43. }
    44. })
    45. server.listen(8080, (err, address) => {
    46. if (err) {
    47. console.error(err)
    48. process.exit(0)
    49. }
    50. console.log(`Server listening at ${address}`)
    51. })

    要特别关注文件顶部的导入。虽然看上去有些多余,但你必须同时导入 schema 与生成的接口。

真棒!现在你就能同时运用 JSON Schema 与 TypeScript 的定义了。

json-schema-to-ts

不想基于 schema 生成类型,而是直接使用它们的话,你可以考虑 json-schema-to-ts 模块。

安装该模块为 dev-dependency:

  1. npm install -D json-schema-to-ts

你可以像定义正常的对象一样定义 schema。但得注意要用 const 来定义,原因见该模块的文档。

  1. const todo = {
  2. type: 'object',
  3. properties: {
  4. name: { type: 'string' },
  5. description: { type: 'string' },
  6. done: { type: 'boolean' },
  7. },
  8. required: ['name'],
  9. } as const;

通过类型 FromSchema 你可以基于 schema 构建一个类型,并在函数中使用它。

  1. fastify.post<{ Body: FromSchema<typeof todo> }>(
  2. '/todo',
  3. {
  4. schema: {
  5. body: todo,
  6. response: {
  7. 201: {
  8. type: 'string',
  9. },
  10. },
  11. }
  12. },
  13. async (request, reply): Promise<void> => {
  14. /*
  15. request.body 的类型如下:
  16. {
  17. [x: string]: unknown;
  18. description?: string;
  19. done?: boolean;
  20. name: string;
  21. }
  22. */
  23. request.body.name // 不会抛出类型错误
  24. request.body.notthere // 会抛出类型错误
  25. reply.status(201).send();
  26. },
  27. );

插件

拓展性强的插件生态系统是 Fastify 最突出的特性之一。插件完全支持类型系统,并利用了声明合并 (declaration merging) 模式的优势。下面的例子将分为三个部分:用 TypeScript 编写 Fastify 插件,为插件编写类型定义,以及在 TypeScript 项目中使用插件。

用 TypeScript 编写 Fastify 插件

  1. 初始化新的 npm 项目,并安装必需的依赖。

    1. npm init -y
    2. npm i fastify fastify-plugin
    3. npm i -D typescript @types/node
  2. package.json"scripts" 中加上一行 build"types" 中写入 'index.d.ts'

    1. {
    2. "types": "index.d.ts",
    3. "scripts": {
    4. "build": "tsc -p tsconfig.json"
    5. }
    6. }
  3. 初始化 TypeScript 配置文件:

    1. npx typescript --init

    文件生成后,启用 "compilerOptions" 对象中的 "declaration" 选项。

    1. {
    2. "compileOptions": {
    3. "declaration": true
    4. }
    5. }
  4. 新建 index.ts 文件,在这里编写插件代码。

  5. index.ts 中写入以下代码。

    1. import { FastifyPluginCallback, FastifyPluginAsync } from 'fastify'
    2. import fp from 'fastify-plugin'
    3. // 利用声明合并,将插件的属性加入合适的 fastify 接口。
    4. declare module 'fastify' {
    5. interface FastifyRequest {
    6. myPluginProp: string
    7. }
    8. interface FastifyReply {
    9. myPluginProp: number
    10. }
    11. }
    12. // 定义选项
    13. export interface MyPluginOptions {
    14. myPluginOption: string
    15. }
    16. // 使用回调函数定义插件
    17. const myPluginCallback: FastifyPluginCallback<MyPluginOptions> = (fastify, options, done) => {
    18. fastify.decorateRequest('myPluginProp', 'super_secret_value')
    19. fastify.decorateReply('myPluginProp', options.myPluginOption)
    20. done()
    21. }
    22. // 使用 promise 定义插件
    23. const myPluginAsync: FastifyPluginAsync<MyPluginOptions> = async (fastify, options) => {
    24. fastify.decorateRequest('myPluginProp', 'super_secret_value')
    25. fastify.decorateReply('myPluginProp', options.myPluginOption)
    26. }
    27. // 使用 fastify-plugin 导出插件
    28. export default fp(myPluginCallback, '3.x')
    29. // 或者
    30. // export default fp(myPluginAsync, '3.x')
  6. 运行 npm run build 编译,生成 JavaScript 源文件以及类型定义文件。

  7. 如此一来,插件便完工了。你可以[发布到 npm] 或直接本地使用。

    并非将插件发布到 npm 才能 使用。你可以将其放在 Fastify 项目内,并像引用任意代码一样引用它!请确保声明文件在项目编译的范围内,以便能被 TypeScript 处理器使用。

为插件编写类型定义

以下例子是为 JavaScript 编写的 Fastify 插件所作,展示了如何在插件中加入 TypeScript 支持,以方便用户使用。

  1. 初始化新的 npm 项目,并安装必需的依赖。

    1. npm init -y
    2. npm i fastify-plugin
  2. 新建 index.jsindex.d.ts

  3. 将这两个文件写入 package.json 的 maintypes 中 (文件名不一定为 index,但推荐都使用这个名字):

    1. {
    2. "main": "index.js",
    3. "types": "index.d.ts"
    4. }
  4. index.js 中加入以下代码:

    1. // 极力推荐使用 fastify-plugin 包装插件
    2. const fp = require('fastify-plugin')
    3. function myPlugin (instance, options, done) {
    4. // 用自定义函数 myPluginFunc 装饰 fastify 实例
    5. instance.decorate('myPluginFunc', (input) => {
    6. return input.toUpperCase()
    7. })
    8. done()
    9. }
    10. module.exports = fp(myPlugin, {
    11. fastify: '3.x',
    12. name: 'my-plugin' // 被 fastify-plugin 用来获取属性名
    13. })
  5. index.d.ts 中加入以下代码:

    1. import { FastifyPlugin } from 'fastify'
    2. interface PluginOptions {
    3. //...
    4. }
    5. // 你可以导出任意内容
    6. // 在此,我们导出之前添加的装饰器
    7. export interface myPluginFunc {
    8. (input: string): string
    9. }
    10. // 利用声明合并将自定义属性加入 Fastify 的类型系统
    11. declare module 'fastify' {
    12. interface FastifyInstance {
    13. myPluginFunc: myPluginFunc
    14. }
    15. }
    16. // fastify-plugin 会自动添加具名导出,因此请确保加上该类型。
    17. // 如果缺少 `module.exports.myPlugin`,变量名会通过 `options.name` 属性获取。
    18. export const myPlugin: FastifyPlugin<PluginOptions>
    19. // fastify-plugin 会自动在导出的插件上添加 `.default` 属性。详见下文。
    20. export default myPlugin

注意:v2.3.0 及以上版本的 fastify-plugin 会自动给导出的插件添加 default 属性以及具名导出。为了更好的开发体验,请确保在类型文件中加上了 export defaultexport const myPlugin。完整的例子可以查看 fastify-swagger

这样一来,该插件便能被任意 TypeScript 项目使用了!

Fastify 的插件系统允许开发者装饰 Fastify 以及 request/reply 的实例。更多信息请见声明合并与泛型继承一文。

使用插件

在 TypeScript 中使用插件和在 JavaScript 中使用一样简单,只需要用到 import/from 而已,除了一个特殊情况。

Fastify 插件使用声明合并来修改已有的 Fastify 类型接口 (详见上一个例子)。声明合并没有那么 聪明,只要插件的类型定义在 TypeScript 解释器的范围内,那么不管插件本身是否被使用,这些定义都会被包括。这是 TypeScript 的限制,目前无法规避。

尽管如此,还是有一些建议能帮助改善这种状况:

  • 确保 ESLint 开启了 no-unused-vars,并且所有导入的插件都得到了加载。
  • 通过诸如 depchecknpm-check 的工具来验证所有插件都在项目中得到了使用。

原生 JavaScript 的代码补全

原生 JavaScript 能根据发布好的类型,依照 TypeScript 的 JSDoc 参考 来完成代码补全 (例如 Intellisense)。

举个例子:

  1. /** @type {import('fastify').FastifyPluginAsync<{ optionA: boolean, optionB: string }>} */
  2. module.exports = async function (fastify, { optionA, optionB }) {
  3. fastify.get('/look', () => 'at me');
  4. }

API 类型系统文档

本节详述了所有在 Fastify 3.x 版本中可用的类型。

所有 httphttps 以及 http2 的类型来自 @types/node

泛型的文档包括了其默认值以及约束值。更多关于 TypeScript 泛型的信息请阅读以下文章。

如何导入

Fastify 的 API 都首先来自于 fastify() 方法。在 JavaScript 中,通过 const fastify = require('fastify') 来导入。在 TypeScript 中,建议的做法是使用 import/from 语法,这样类型能得到处理。有如下几种导入的方法。

  1. import fastify from 'fastify'

    • 类型得到了处理,但无法通过点标记 (dot notation) 访问
    • 例子:

      1. import fastify from 'fastify'
      2. const f = fastify()
      3. f.listen(8080, () => { console.log('running') })
    • 通过解构赋值访问类型

      1. import fastify, { FastifyInstance } from 'fastify'
      2. const f: FastifyInstance = fastify()
      3. f.listen(8080, () => { console.log('running') })
    • 主 API 方法也可以使用解构赋值

      1. import { fastify, FastifyInstance } from 'fastify'
      2. const f: FastifyInstance = fastify()
      3. f.listen(8080, () => { console.log('running') })
  2. import * as Fastify from 'fastify'

    • 类型得到了处理,并可通过点标记访问
    • 主 API 方法要用稍微不同的语法调用 (见例子)
    • 例子:

      1. import * as Fastify from 'fastify'
      2. const f: Fastify.FastifyInstance = Fastify.fastify()
      3. f.listen(8080, () => { console.log('running') })
  3. const fastify = require('fastify')

    • 语法有效,也能正确地导入。然而并支持类型
    • 例子:

      1. const fastify = require('fastify')
      2. const f = fastify()
      3. f.listen(8080, () => { console.log('running') })
    • 支持解构,但同样无法处理类型

      1. const { fastify } = require('fastify')
      2. const f = fastify()
      3. f.listen(8080, () => { console.log('running') })

泛型

许多类型定义共用了某些泛型参数。它们都在本节有详尽的描述。

多数定义依赖于 @node/types 中的 httphttpshttp2 模块。

RawServer

底层 Node.js server 的类型。

默认值:http.Server

约束值:http.Serverhttps.Serverhttp2.Http2Serverhttp2.Http2SecureServer

必要的泛型参数 (Enforces generic parameters):RawRequestRawReply

RawRequest

底层 Node.js request 的类型。

默认值:RawRequestDefaultExpression

约束值:http.IncomingMessagehttp2.Http2ServerRequest

RawServer 约束。

RawReply

底层 Node.js response 的类型。

默认值:RawReplyDefaultExpression

约束值:http.ServerResponsehttp2.Http2ServerResponse

RawServer 约束。

Logger

Fastify 日志工具。

默认值:FastifyLoggerOptions

RawServer 约束。

RawBody

为 content-type-parser 方法提供的泛型参数。

约束值:string | Buffer


Fastify

fastify<RawServer, RawRequest, RawReply, Logger>(opts?: FastifyServerOptions): FastifyInstance

源码

Fastify 首要的 API 方法。默认情况下创建一个 HTTP 服务器。通过可辨识联合 (discriminant unions) 及重载的方法 (overload methods),类型系统能自动地根据传递给该方法的选项 (详见下文例子),推断出服务器的类型 (http、https 或 http2)。同时,可拓展的泛型类型系统允许用户拓展底层的 Node.js Server、Request 和 Reply 对象。此外,自定义日志类型则可以运用 Logger 泛型。详见下文的例子和泛型分类说明。

例子 1:标准的 HTTP 服务器

无需指明 Server 的具体类型,因为默认值就是 HTTP 服务器。

  1. import fastify from 'fastify'
  2. const server = fastify()

回顾“从例子中学习”的起步一节的示例来获取更详细的内容。

例子 2:HTTPS 服务器
  1. @types/nodefastify 导入模块。

    1. import fs from 'fs'
    2. import path from 'path'
    3. import fastify from 'fastify'
  2. 按照官方 Node.js https 服务器指南的步骤,创建 key.pemcert.pem 文件。

  3. 实例化一个 Fastify https 服务器,并添加一个路由:

    1. const server = fastify({
    2. https: {
    3. key: fs.readFileSync(path.join(__dirname, 'key.pem')),
    4. cert: fs.readFileSync(path.join(__dirname, 'cert.pem'))
    5. }
    6. })
    7. server.get('/', async function (request, reply) {
    8. return { hello: 'world' }
    9. })
    10. server.listen(8080, (err, address) => {
    11. if (err) {
    12. console.error(err)
    13. process.exit(0)
    14. }
    15. console.log(`Server listening at ${address}`)
    16. })
  4. 构建并运行!执行 curl -k https://localhost:8080 来测试服务。

例子 3:HTTP2 服务器

HTTP2 服务器有两种类型,非安全与安全。两种类型都需要在 options 对象中设置 http2 属性的值为 true。设置 https 属性会创建一个安全的 http2 服务器;忽略该属性则创建非安全的服务器。

  1. const insecureServer = fastify({ http2: true })
  2. const secureServer = fastify({
  3. http2: true,
  4. https: {} // 使用 https 服务的 `key.pem` 和 `cert.pem` 文件
  5. })

更多细节详见 Fastify 的 HTTP2 文档。

例子 4:拓展 HTTP 服务器

你不仅可以指定服务器的类型,还可以指定请求与响应的类型,即指定特殊的属性、方法等!在服务器实例化之时指定类型,则之后的实例都可应用自定义的类型。

  1. import fastify from 'fastify'
  2. import http from 'http'
  3. interface customRequest extends http.IncomingMessage {
  4. mySpecialProp: string
  5. }
  6. const server = fastify<http.Server, customRequest>()
  7. server.get('/', async (request, reply) => {
  8. const someValue = request.raw.mySpecialProp // 由于 `customRequest` 接口的存在,TypeScript 能知道这是一个字符串
  9. return someValue.toUpperCase()
  10. })
例子 5:指定日志类型

Fastify 使用 Pino 作为日志工具。其中一些属性可以在构建 Fastify 实例时,在 logger 字段中配置。如果需要的属性未被暴露出来,你也能通过将一个外部配置好的 Pino 实例 (或其他兼容的日志工具) 传给这个字段,来配置这些属性。这么做也允许你自定义序列化工具,详见日志的文档。

要使用 Pino 的外部实例,请将 @types/pino 添加到 devDependencies 中,并把实例传给 logger 字段:

  1. import fastify from 'fastify'
  2. import pino from 'pino'
  3. const server = fastify({
  4. logger: pino({
  5. level: 'info',
  6. redact: ['x-userinfo'],
  7. messageKey: 'message'
  8. })
  9. })
  10. server.get('/', async (request, reply) => {
  11. server.log.info('log message')
  12. return 'another message'
  13. })

fastify.HTTPMethods

源码

'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS' 的联合类型 (Union type)

fastify.RawServerBase

源码

依赖于 @types/node 的模块 httphttpshttp2

http.Server | https.Server | http2.Http2Server | http2.Http2SecureServer 的联合类型

fastify.RawServerDefault

源码

依赖于 @types/node 的模块 http

http.Server 的类型别名


fastify.FastifyServerOptions<RawServer, Logger>

源码

Fastify 服务器实例化时,调用 fastify() 方法使用到的接口。泛型参数 RawServerLogger 会随此方法向下传递。

关于用 TypeScript 实例化一个 Fastify 服务器的例子,请见 fastify 主方法的类型定义。

fastify.FastifyInstance<RawServer, RawRequest, RequestGeneric, Logger>

源码

表示 Fastify 服务器对象的接口,fastify() 方法的返回值。假如你使用了 decorate 方法,借由声明合并可以拓展该接口。

通过泛型级联 (generic cascading),实例上所有的方法都能继承实例化时的泛型属性。这意味着只要指定了服务器、请求或响应的类型,所有方法都能随之确定这些对象的类型。

具体说明请看“从例子中学习”一节,或 fastify 方法中更简洁的例子。


Request

fastify.FastifyRequest<RequestGeneric, RawServer, RawRequest>

源码

该接口包含了 Fastify 请求对象的属性。这些属性无视请求类型 (http 或 http2),也无关路由层级。因此在 GET 请求中访问 request.body 并不会抛错 (假如 GET 有 body 😉)。

假如你需要为 FastifyRequest 对象添加自定义属性 (例如使用 [decorateRequest][DecorateRequest] 方法时),你应该针对该接口应用声明合并。

FastifyRequest 里有基本的范例。更详细的例子请见“从例子中学习”的插件一节。

例子
  1. import fastify from 'fastify'
  2. const server = fastify()
  3. server.decorateRequest('someProp', 'hello!')
  4. server.get('/', async (request, reply) => {
  5. const { someProp } = request // 需要通过声明合并将该属性添加到 request 接口上
  6. return someProp
  7. })
  8. // 以下声明必须在 typescript 解释器的作用域内
  9. declare module 'fastify' {
  10. interface FastifyRequest { // 引用的是接口而非类型
  11. someProp: string
  12. }
  13. }
  14. // 你也可以如此定义 request 的类型
  15. type CustomRequest = FastifyRequest<{
  16. Body: { test: boolean };
  17. }>
  18. server.get('/typedRequest', async (request: CustomRequest, reply: FastifyReply) => {
  19. return request.body.test
  20. })
fastify.RequestGenericInterface

源码

Fastify 的请求对象有四个动态属性:bodyparamsquery 以及 headers,它们对应的类型可以通过该接口设定。这是具名属性接口,允许开发者忽略他们不想指定的类型。所有忽略的属性默认为 unknown。四个属性名为:BodyQuerystringParamsHeaders

  1. import fastify, { RequestGenericInterface } from 'fastify'
  2. const server = fastify()
  3. interface requestGeneric extends RequestGenericInterface {
  4. Querystring: {
  5. name: string
  6. }
  7. }
  8. server.get<requestGeneric>('/', async (request, reply) => {
  9. const { name } = request.query // 此时 query 属性上有了 name
  10. return name.toUpperCase()
  11. })

在“从例子中学习”的 JSON Schema 一节中,你能找到更具体的范例。

fastify.RawRequestDefaultExpression<RawServer>

源码

依赖于 @types/node 的模块 httphttpshttp2

泛型参数 RawServer 的默认值为 RawServerDefault

如果 RawServer 的类型为 http.Serverhttps.Server,那么该表达式返回 http.IncomingMessage,否则返回 http2.Http2ServerRequest

  1. import http from 'http'
  2. import http2 from 'http2'
  3. import { RawRequestDefaultExpression } from 'fastify'
  4. RawRequestDefaultExpression<http.Server> // -> http.IncomingMessage
  5. RawRequestDefaultExpression<http2.Http2Server> // -> http2.Http2ServerRequest

Reply

fastify.FastifyReply<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>

源码

该接口包含了 Fastify 添加到 Node.js 标准的 reply 对象上的属性。这些属性和 reply 对象的类型 (http 或 http2) 无关。

假如你需要为 FastifyReply 对象添加自定义属性 (例如使用 decorateReply 方法时),你应该针对该接口应用声明合并。

FastifyReply 里有基本的范例。更详细的例子请见“从例子中学习”的插件一节。

例子
  1. import fastify from 'fastify'
  2. const server = fastify()
  3. server.decorateReply('someProp', 'world')
  4. server.get('/', async (request, reply) => {
  5. const { someProp } = reply //需要通过声明合并将该属性添加到 reply 接口上
  6. return someProp
  7. })
  8. // 以下声明必须在 typescript 解释器的作用域内
  9. declare module 'fastify' {
  10. interface FastifyReply { // 引用的是接口而非类型
  11. someProp: string
  12. }
  13. }
fastify.RawReplyDefaultExpression<RawServer>

源码

依赖于 @types/node 的模块 httphttpshttp2

泛型参数 RawServer 的默认值为 RawServerDefault

如果 RawServer 的类型为 http.Serverhttps.Server,那么该表达式返回 http.ServerResponse,否则返回 http2.Http2ServerResponse

  1. import http from 'http'
  2. import http2 from 'http2'
  3. import { RawReplyDefaultExpression } from 'fastify'
  4. RawReplyDefaultExpression<http.Server> // -> http.ServerResponse
  5. RawReplyDefaultExpression<http2.Http2Server> // -> http2.Http2ServerResponse

插件

通过插件,用户能拓展 Fastify 的功能。一个插件可以是一组路由,也可以是一个装饰器,或其它逻辑。要激活一个插件,需调用 fastify.register() 方法。

创建插件时,我们推荐使用 fastify-plugin。在“从例子中学习”的插件一节中有使用 TypeScript 创建插件的指南。

fastify.FastifyPluginCallback<Options>

源码

fastify.register() 使用的接口方法定义。

fastify.FastifyPluginAsync<Options>

源码

fastify.register() 使用的接口方法定义。

fastify.FastifyPlugin<Options>

源码

fastify.register() 使用的接口方法定义。

通用的 FastifyPlugin 已不推荐使用,取而代之的是上述 FastifyPluginCallback 以及 FastifyPluginAsync。这是因为 FastifyPlugin 无法正确推断出异步函数的类型。

fastify.FastifyPluginOptions

源码

一个用于约束 fastify.register()options 参数为对象类型的宽松类型对象 (loosely typed object)。在创建插件时,将插件的选项定义为此接口 (interface MyPluginOptions extends FastifyPluginOptions),传递给 register 方法。


Register

fastify.FastifyRegister(plugin: FastifyPluginCallback, opts: FastifyRegisterOptions)

源码

fastify.FastifyRegister(plugin: FastifyPluginAsync, opts: FastifyRegisterOptions)

源码

fastify.FastifyRegister(plugin: FastifyPlugin, opts: FastifyRegisterOptions)

源码

指定 fastify.register() 类型的类型接口,返回一个拥有默认值为 FastifyPluginOptionsOptions 泛型的函数签名。当调用此函数时,根据 FastifyPlugin 参数能推断出该泛型,因此不必特别指定。options 参数是插件选项以及 prefix: stringlogLevel (LogLevel) 两个属性的交叉类型。

以下例子展示了 options 的推断:

  1. const server = fastify()
  2. const plugin: FastifyPlugin<{
  3. option1: string;
  4. option2: boolean;
  5. }> = function (instance, opts, done) { }
  6. fastify().register(plugin, {}) // 错误 - options 对象缺失了必要的属性
  7. fastify().register(plugin, { option1: '', option2: true }) // OK - options 对象包括了必要的属性

在“从例子中学习”的插件一节中有使用 TypeScript 创建插件的详细示例。

fastify.FastifyRegisterOptions

源码

该类型是 Options 泛型以及包括 prefix: stringlogLevel (LogLevel) 两个可选属性的未导出接口 RegisterOptions 的交叉类型。也可以被指定为返回前述交叉类型的函数。


日志

请在指定日志类型的例子中,查阅自定义日志工具的细节。

fastify.FastifyLoggerOptions<RawServer, RawRequest, RawReply>

源码

Fastify 内建日志工具的接口定义,模仿了 Pino.js 的接口定义。当通过服务器选项启用日志时,参照日志文档使用它。

fastify.FastifyLogFn

源码

一个重载函数接口,实现 Fastify 调用日志的方法,会传递到所有 FastifyLoggerOptions 中启用的日志级别属性。

fastify.LogLevel

源码

'info' | 'error' | 'debug' | 'fatal' | 'warn' | 'trace' 的联合类型


Context

context 类型定义和类型系统中其它高度动态化的部分类似。路由上下文 (context) 在路由函数内可用。

fastify.FastifyContext

源码

有一个默认为 unknown 的必填属性 config 的接口。可用泛型或重载来指定。

此类型定义可能尚不完善。假如你有改进它的建议,欢迎在 fastify/fastify 仓库发布一个 issue。感谢!


路由

Fastify 的其中一条核心原则便是强大的路由。本节中多数的类型被 Fastify 实例的 .route.get/.post 等方法内在地使用。

fastify.RouteHandlerMethod<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>

源码

路由控制函数的类型声明,有两个参数:类型为 FastifyRequestrequest,以及类型为 FastifyReplyreply。泛型参数会传递给这些参数。当控制函数为同步函数时,返回 void,异步则返回 Promise<any>

fastify.RouteOptions<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>

源码

拓展了 RouteShorthandOptions 的接口,并添加以下三个必填属性:

  1. method 单个或一组 HTTP 方法
  2. url 路由路径字符串。
  3. handler 路由控制函数,详见 [RouteHandlerMethod][]。
fastify.RouteShorthandMethod<RawServer, RawRequest, RawReply>

源码

一个重载函数接口,用于定义 .get/.post 等简写方法的三种不同形式。

fastify.RouteShorthandOptions<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>

源码

包含所有路由基本选项的接口。所有属性都是可选的。该接口是 RouteOptions 和 RouteShorthandOptionsWithHandler 接口的基础。

fastify.RouteShorthandOptionsWithHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>

源码

向 RouteShorthandOptions 接口添加一个必填属性:handler,类型为 RouteHandlerMethod。


Parsers

RawBody

一个为 stringBuffer 的泛型类型。

fastify.FastifyBodyParser<RawBody, RawServer, RawRequest>

源码

定义 body 解析器 (body parser) 的函数类型。使用 RawBody 泛型指定被解析的 body。

fastify.FastifyContentTypeParser<RawServer, RawRequest>

源码

定义 body 解析器的函数类型。使用 RawRequest 泛型定义 content。

fastify.AddContentTypeParser<RawServer, RawRequest>

源码

addContentTypeParser 方法的重载函数接口。当 parseAs 出现在 opts 参数中时,parser 参数使用 [FastifyBodyParser][],否则使用 [FastifyContentTypeParser][]。

fastify.hasContentTypeParser

源码

检查指定 content type 解析器是否存在的方法。


错误

fastify.FastifyError

源码

FastifyError 是自定义的错误对象,包括了状态码及校验结果。

拓展了 Node.js 的 Error 类型,并加入了两个可选属性:statusCode: numbervalidation: ValiationResult[]

fastify.ValidationResult

源码

路由校验内在地依赖于 Ajv,一个高性能 JSON schema 校验工具。

该接口会传递给 FastifyError 的实例。


钩子

fastify.onRequestHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

onRequest 是第一个被执行的钩子,其下一个钩子为 preParsing

注意:在 onRequest 钩子中,request.body 永远为 null,因为此时 body 尚未解析 (解析发生在 preHandler 钩子之前)。

fastify.preParsingHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

preParsing 是第二个钩子,前一个为 onRequest,下一个为 preValidation

注意:在 preParsing 钩子中,request.body 永远为 null,因为此时 body 尚未解析 (解析发生在 preValidation 钩子之前)。

注意:你应当给返回的 stream 添加 receivedEncodedLength 属性。这是为了通过比对请求头的 Content-Length,来精确匹配请求的 payload。理想情况下,每收到一块数据都应该更新该属性。

fastify.preValidationHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

preValidation 是第三个钩子,前一个为 preParsing,下一个为 preHandler

注意:在 preValidation 钩子中,request.body 永远为 null,因为此时 body 尚未解析 (解析发生在 preHandler 钩子之前)。

fastify.preHandlerHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

preHandler 是第四个钩子,前一个为 preValidation,下一个为 preSerialization

fastify.preSerializationHookHandler<PreSerializationPayload, RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, payload: PreSerializationPayload, done: (err: FastifyError | null, res?: unknown) => void): Promise<unknown> | void

源码

preSerialization 是第五个钩子,前一个为 preHandler,下一个为 onSend

注:当 payload 为 string、Buffer、stream 或 null 时,该钩子不会执行。

fastify.onSendHookHandler<OnSendPayload, RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, payload: OnSendPayload, done: (err: FastifyError | null, res?: unknown) => void): Promise<unknown> | void

源码

你可以在 onSend 钩子中变更 payload。这是第六个钩子,前一个为 preSerialization,下一个为 onResponse

注:你只能将 payload 改为 string、Buffer、stream 或 null。

fastify.onResponseHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

onResponse 是第七个,也是最后一个钩子,前一个为 onSend

该钩子在响应发出后执行,因此无法再发送更多数据了。但是你可以在此向外部服务发送数据,执行收集数据之类的任务。

fastify.onErrorHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(request: FastifyRequest, reply: FastifyReply, error: FastifyError, done: () => void): Promise<unknown> | void

源码

该钩子可用于自定义错误日志,或当发生错误时添加特定的 header。

该钩子并不是为了变更错误而设计的,且调用 reply.send 会抛出一个异常。

它只会在 customErrorHandler 向用户发送错误之后被执行 (要注意的是,默认的 customErrorHandler 总是会发送错误)。

注意:与其他钩子不同,该钩子不支持向 done 函数传递错误。

fastify.onRouteHookHandler<RawServer, RawRequest, RawReply, RequestGeneric, ContextConfig>(opts: RouteOptions & { path: string; prefix: string }): Promise<unknown> | void

源码

当注册一个新的路由时被触发。它的监听函数拥有一个唯一的参数:routeOptions 对象。该接口是同步的,因此,监听函数不接受回调作为参数。

fastify.onRegisterHookHandler<RawServer, RawRequest, RawReply, Logger>(instance: FastifyInstance, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

当注册一个新的插件,或创建了新的封装好的上下文后被触发。该钩子在注册的代码之前被执行。

当你的插件需要知晓上下文何时创建完毕,并操作它们时,可以使用这一钩子。

注:被 fastify-plugin 所封装的插件不会触发该钩子。

fastify.onCloseHookHandler<RawServer, RawRequest, RawReply, Logger>(instance: FastifyInstance, done: (err?: FastifyError) => void): Promise<unknown> | void

源码

使用 fastify.close() 停止服务器时被触发。当插件需要一个 “shutdown” 事件时有用,例如关闭一个数据库连接。