Fastify

TypeScript

尽管 Fastify 自带了 typings 声明文件,你可能仍然需要根据所使用的 Node.js 版本来安装 @types/node

Types 支持

我们关注 TypeScript 社区,当前也有一名团队的核心成员正在重做所有的 types。 我们尽自己最大的努力来保证 typings 文件与最新的 API 同步,但并不能完全避免不同步的情况发生。
幸运的是这是个开源项目,你可以参与修复。我们十分欢迎你的贡献,并会尽快发布补丁。请看 贡献 指南吧!

插件有可能包含 typings,也可能没有。具体信息请参阅 插件类型

示例

以下 TypeScript 的程序示例和 JavaScript 版本的示例紧密相似:

  1. import * as fastify from 'fastify'
  2. import { Server, IncomingMessage, ServerResponse } from 'http'
  3. // 创建一个 http 服务器,将 http 对应版本所使用的 typings 传递过去。
  4. // 这么做我们便能获知路由底层 http 对象的结构。
  5. // 如果使用 http2,你应该传递 <http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>
  6. // 如果使用 https,则是 http2.Http2SecureServer 或 http.SecureServer,而不是 Server。
  7. const server: fastify.FastifyInstance<Server, IncomingMessage, ServerResponse> = fastify({})
  8. const opts: fastify.RouteShorthandOptions = {
  9. schema: {
  10. response: {
  11. 200: {
  12. type: 'object',
  13. properties: {
  14. pong: {
  15. type: 'string'
  16. }
  17. }
  18. }
  19. }
  20. }
  21. }
  22. server.get('/ping', opts, (request, reply) => {
  23. console.log(reply.res) // 带有正确 typings 的 http.ServerResponse!
  24. reply.code(200).send({ pong: 'it worked!' })
  25. })

一般类型的参数

你不但可以校验 querystring、url 参数、body 以及 header,你还可以覆盖 request 接口中定义的默认类型:

  1. import * as fastify from 'fastify'
  2. const server = fastify({})
  3. interface Query {
  4. foo?: number
  5. }
  6. interface Params {
  7. bar?: string
  8. }
  9. interface Body {
  10. baz?: string
  11. }
  12. interface Headers {
  13. a?: string
  14. }
  15. const opts: fastify.RouteShorthandOptions = {
  16. schema: {
  17. querystring: {
  18. type: 'object',
  19. properties: {
  20. foo: {
  21. type: 'number'
  22. }
  23. }
  24. },
  25. params: {
  26. type: 'object',
  27. properties: {
  28. bar: {
  29. type: 'string'
  30. }
  31. }
  32. },
  33. body: {
  34. type: 'object',
  35. properties: {
  36. baz: {
  37. type: 'string'
  38. }
  39. }
  40. },
  41. headers: {
  42. type: 'object',
  43. properties: {
  44. a: {
  45. type: 'string'
  46. }
  47. }
  48. }
  49. }
  50. }
  51. server.get<Query, Params, Headers, Body>('/ping/:bar', opts, (request, reply) => {
  52. console.log(request.query) // 这是 Query 类型
  53. console.log(request.params) // 这是 Params 类型
  54. console.log(request.headers) // 这是 Headers 类型
  55. console.log(request.body) // 这是 Body 类型
  56. reply.code(200).send({ pong: 'it worked!' })
  57. })

所有的一般类型都是可选的,因此你可以只传递你使用 schema 校验的类型:

  1. import * as fastify from 'fastify'
  2. const server = fastify({})
  3. interface Params {
  4. bar?: string
  5. }
  6. const opts: fastify.RouteShorthandOptions = {
  7. schema: {
  8. params: {
  9. type: 'object',
  10. properties: {
  11. bar: {
  12. type: 'string'
  13. }
  14. }
  15. },
  16. }
  17. }
  18. server.get<fastify.DefaultQuery, Params, unknown>('/ping/:bar', opts, (request, reply) => {
  19. console.log(request.query) // 这是 fastify.DefaultQuery 类型
  20. console.log(request.params) // 这是 Params 类型
  21. console.log(request.headers) // 这是未知的类型
  22. console.log(request.body) // 这是 fastify.DefaultBody 类型,因为 typescript 会使用默认类型
  23. reply.code(200).send({ pong: 'it worked!' })
  24. })
  25. // 假设你不校验 querystring、body 或者 header,
  26. // 最好将类型设为 `unknown`。但这个选择取决于你。
  27. // 下面的例子展示了这一做法,它可以避免你搬起石头砸自己的脚。
  28. // 换句话说,就是别使用不去校验的类型。
  29. server.get<unknown, Params, unknown, unknown>('/ping/:bar', opts, (request, reply) => {
  30. console.log(request.query) // 这是未知的类型
  31. console.log(request.params) // 这是 Params 类型
  32. console.log(request.headers) // 这是未知的类型
  33. console.log(request.body) // 这是未知的类型
  34. reply.code(200).send({ pong: 'it worked!' })
  35. })

HTTP 原型

默认情况下,Fastify 会根据你给的配置决定使用 http 的哪个版本。出于某种原因需要覆盖的话,你可以这么做:

  1. interface CustomIncomingMessage extends http.IncomingMessage {
  2. getClientDeviceType: () => string
  3. }
  4. // 将覆盖的 http 原型传给 Fastify
  5. const server: fastify.FastifyInstance<http.Server, CustomIncomingMessage, http.ServerResponse> = fastify()
  6. server.get('/ping', (request, reply) => {
  7. // 使用自定义的 http 原型方法
  8. const clientDeviceType = request.raw.getClientDeviceType()
  9. reply.send({ clientDeviceType: `you called this endpoint from a ${clientDeviceType}` })
  10. })

在这个例子中,我们传递了一个经过修改的 http.IncomingMessage 接口,该接口在程序的其他地方得到了扩展。

贡献

和 TypeScript 相关的改动可以被归入下列类别:

  • Core - Fastify 的 typings 文件
  • Plugins - Fastify 插件

记得要先阅读 CONTRIBUTING.md 文件,确保行事顺利!

核心类型

当更新核心类型时,你应当向本仓库发一个 PR。请确保:

  1. 将改动反映在 examples/typescript-server.ts 文件中 (当需要时)
  2. 更新 test/types/index.ts 来验证改动是否成功

插件类型

和 fastify 仓库一样,由 GitHub 上的 fastify 组织所维护的插件,应当自带 typings 文件。 目前一些插件还没有 typings 文件,我们很欢迎你参与其中。typings 的例子请看 fastify-cors 仓库。

第三方插件可能自带 typings 文件,或存放于 DefinitelyTyped 之上。请记住,如果你写了一个插件,也请选择上述两种途径之一来存放 typings 文件!从 DefinitelyTyped 安装 typings 的方法可以在这里找到。

一些类型可能还不可用,因此尽管去贡献吧。

编写插件类型

扩展了 FastifyRequestFastifyReplyFastifyInstance 对象的许多插件,可以通过如下方式获取。

以下代码展示了 fastify-static 插件的 typings。

  1. /// <reference types="node" />
  2. // 导入 fastify typings
  3. import * as fastify from 'fastify';
  4. // 导入必需的 http, http2, https typings
  5. import { Server, IncomingMessage, ServerResponse } from "http";
  6. import { Http2SecureServer, Http2Server, Http2ServerRequest, Http2ServerResponse } from "http2";
  7. import * as https from "https";
  8. type HttpServer = Server | Http2Server | Http2SecureServer | https.Server;
  9. type HttpRequest = IncomingMessage | Http2ServerRequest;
  10. type HttpResponse = ServerResponse | Http2ServerResponse;
  11. // 拓展 fastify typings
  12. declare module "fastify" {
  13. interface FastifyReply<HttpResponse> {
  14. sendFile(filename: string): FastifyReply<HttpResponse>;
  15. }
  16. }
  17. // 使用 fastify.Plugin 声明插件的类型
  18. declare function fastifyStatic(): fastify.Plugin<
  19. Server,
  20. IncomingMessage,
  21. ServerResponse,
  22. {
  23. root: string;
  24. prefix?: string;
  25. serve?: boolean;
  26. decorateReply?: boolean;
  27. schemaHide?: boolean;
  28. setHeaders?: (...args: any[]) => void;
  29. redirect?: boolean;
  30. wildcard?: boolean | string;
  31. // `send` 的选项
  32. acceptRanges?: boolean;
  33. cacheControl?: boolean;
  34. dotfiles?: boolean;
  35. etag?: boolean;
  36. extensions?: string[];
  37. immutable?: boolean;
  38. index?: string[];
  39. lastModified?: boolean;
  40. maxAge?: string | number;
  41. }
  42. >;
  43. declare namespace fastifyStatic {
  44. interface FastifyStaticOptions {}
  45. }
  46. // 导出插件类型
  47. export = fastifyStatic;

现在你便可以如此使用插件了:

  1. import * as Fastify from 'fastify'
  2. import * as fastifyStatic from 'fastify-static'
  3. const app = Fastify()
  4. // 这里的配置项会类型检查
  5. app.register(fastifyStatic, {
  6. acceptRanges: true,
  7. cacheControl: true,
  8. decorateReply: true,
  9. dotfiles: true,
  10. etag: true,
  11. extensions: ['.js'],
  12. immutable: true,
  13. index: ['1'],
  14. lastModified: true,
  15. maxAge: '',
  16. prefix: '',
  17. root: '',
  18. schemaHide: true,
  19. serve: true,
  20. setHeaders: (res, pathName) => {
  21. res.setHeader('some-header', pathName)
  22. }
  23. })
  24. app.get('/file', (request, reply) => {
  25. // 使用上文定义的 FastifyReply.sendFile 方法
  26. reply.sendFile('some-file-name')
  27. })

给我们所有的插件加上 typings 需要社区的努力,因此尽管来贡献吧!