Fastify

    使用 npm 安装:

    使用 yarn 安装:

    第一个服务器

    让我们开始编写第一个服务器吧:

    1. // 加载框架并新建实例
    2. const fastify = require('fastify')({
    3. logger: true
    4. })
    5. // 声明路由
    6. fastify.get('/', function (request, reply) {
    7. reply.send({ hello: 'world' })
    8. })
    9. // 启动服务!
    10. fastify.listen(3000, function (err, address) {
    11. if (err) {
    12. fastify.log.error(err)
    13. process.exit(1)
    14. }
    15. fastify.log.info(`server listening on ${address}`)
    16. })

    更喜欢使用 async/await?Fastify 对其提供了开箱即用的支持。
    (我们还建议使用 来避免文件描述符 (file descriptor) 及内存的泄露)

    1. const fastify = require('fastify')()
    2. fastify.get('/', async (request, reply) => {
    3. return { hello: 'world' }
    4. })
    5. const start = async () => {
    6. try {
    7. await fastify.listen(3000)
    8. } catch (err) {
    9. fastify.log.error(err)
    10. process.exit(1)
    11. }
    12. }
    13. start()

    如此简单,棒极了!
    可是,一个复杂的应用需要比上例多得多的代码。当你从头开始构建一个应用时,会遇到一些典型的问题,如多个文件的操作、异步引导,以及代码结构的布置。
    幸运的是,Fastify 提供了一个易于使用的平台,它能帮助你解决不限于上述的诸多问题!

    第一个插件

    就如同在 JavaScript 中一切皆为对象,在 Fastify 中,一切都是插件 (plugin)。
    在深入之前,先来看看插件系统是如何工作的吧!
    让我们新建一个基本的服务器,但这回我们把路由 (route) 的声明从入口文件转移到一个外部文件。(参阅)。

    1. const fastify = require('fastify')({
    2. logger: true
    3. })
    4. fastify.listen(3000, function (err, address) {
    5. if (err) {
    6. fastify.log.error(err)
    7. process.exit(1)
    8. }
    9. fastify.log.info(`server listening on ${address}`)
    10. })

    这个例子调用了 API,它是 Fastify 框架的核心,也是添加路由、插件等的唯一方法。

    让我们重写上述示例,加入一个数据库连接。
    (在这里我们用简单的例子来说明,对于健壮的方案请考虑使用 fastify-mongo 或 Fastify 中的其他插件)

    server.js

    1. const fastify = require('fastify')({
    2. logger: true
    3. })
    4. fastify.register(require('./our-db-connector'), {
    5. url: 'mongodb://localhost:27017/'
    6. })
    7. fastify.register(require('./our-first-route'))
    8. fastify.listen(3000, function (err, address) {
    9. if (err) {
    10. fastify.log.error(err)
    11. process.exit(1)
    12. }
    13. fastify.log.info(`server listening on ${address}`)
    14. })

    our-db-connector.js

    1. const fastifyPlugin = require('fastify-plugin')
    2. const MongoClient = require('mongodb').MongoClient
    3. async function dbConnector (fastify, options) {
    4. const url = options.url
    5. delete options.url
    6. const db = await MongoClient.connect(url, options)
    7. fastify.decorate('mongo', db)
    8. }
    9. // 用 fastify-plugin 包装插件,以使插件中声明的装饰器、钩子函数及中间件暴露在根作用域里。
    10. module.exports = fastifyPlugin(dbConnector)

    our-first-route.js

    1. async function routes (fastify, options) {
    2. const database = fastify.mongo.db('db')
    3. const collection = database.collection('test')
    4. fastify.get('/', async (request, reply) => {
    5. return { hello: 'world' }
    6. })
    7. fastify.get('/search/:id', async (request, reply) => {
    8. const result = await collection.findOne({ id: request.params.id })
    9. if (result.value === null) {
    10. throw new Error('Invalid value')
    11. }
    12. return result.value
    13. })
    14. }
    15. module.exports = routes

    哇,真是快啊!
    介绍了一些新概念后,让我们回顾一下迄今为止都做了些什么吧。
    如你所见,我们可以使用 register 来注册数据库连接器或者路由。 这是 Fastify 最棒的特性之一了!它使得插件按声明的顺序来加载,唯有当前插件加载完毕后,才会加载下一个插件。如此,我们便可以在第一个插件中注册数据库连接器,并在第二个插件中使用它。(参见 这里 了解如何处理插件的作用域)。 当调用函数 fastify.listen()fastify.inject()fastify.ready() 时,插件便开始加载了。

    我们还用到了 decorate API。现在来看看这一 API 是什么,以及它是如何运作的吧。 考虑下需要在应用的不同部分使用相同的代码或库的场景。一种解决方案便是按需引入这些代码或库。哈,这固然可行,但却因为重复的代码和麻烦的重构让人苦恼。
    为了解决上述问题,Fastify 提供了 decorate API。它允许你在 Fastify 的命名空间下添加自定义对象,如此一来,你就可以在所有地方直接使用这些对象了。

    更深入的内容,例如插件如何运作、如何新建,以及使用 Fastify 全部的 API 去处理复杂的异步引导的细节,请看。

    为了保证应用的行为一致且可预测,我们强烈建议你采用以下的顺序来组织代码:

    1. └── 来自 Fastify 生态的插件
    2. └── 你自己的插件
    3. └── 装饰器
    4. └── 你的服务应用

    这确保了你总能访问当前作用域下声明的所有属性。
    如前文所述,Fastify 提供了一个可靠的封装模型,它能帮助你的应用成为单一且独立的服务。假如你要为某些路由单独地注册插件,只需复写上述的结构就足够了。

    1. └── 你自己的插件
    2. └── 装饰器
    3. └── 钩子函数和中间件
    4. └── 你的服务应用
    5. └── 服务 A
    6. └── 来自 Fastify 生态的插件
    7. └── 你自己的插件
    8. └── 装饰器
    9. └── 钩子函数和中间件
    10. └── 你的服务应用
    11. └── 服务 B
    12. └── 来自 Fastify 生态的插件
    13. └── 你自己的插件
    14. └── 装饰器
    15. └── 钩子函数和中间件
    16. └── 你的服务应用

    验证数据

    数据的验证在我们的框架中是极为重要的一环,也是核心的概念。
    Fastify 使用 验证来访的请求。 让我们来看一个验证路由的例子:

    序列化数据

    Fastify 对 JSON 提供了优异的支持,极大地优化了解析 JSON body 与序列化 JSON 输出的过程。
    在 schema 的选项中设置 response 的值,能够加快 JSON 的序列化 (没错,这很慢!),就像这样:

    1. const opts = {
    2. schema: {
    3. response: {
    4. 200: {
    5. type: 'object',
    6. properties: {
    7. hello: { type: 'string' }
    8. }
    9. }
    10. }
    11. }
    12. }
    13. fastify.get('/', opts, async (request, reply) => {
    14. return { hello: 'world' }
    15. })

    简单地指明 schema,序列化的过程就达到了原先 2-3 倍的速度。这么做同时也保护了潜在的敏感数据不被泄露,因为 Fastify 仅对 schema 里出现的数据进行序列化。 请参阅 获取更多信息。

    Fastify 生来十分精简,也具有高可扩展性。我们相信,一个小巧的框架足以实现一个优秀的应用。
    换句话说,Fastify 并非一个面面俱到的框架,它依赖于自己惊人的生态系统

    测试服务器

    Fastify 并没有提供测试框架,但是我们推荐你在测试中使用 Fastify 的特性及结构。
    更多内容请看测试

    从命令行启动服务器

    感谢 fastify-cli,它让 Fastify 集成到了命令行之中。

    首先,你得安装 fastify-cli:

    1. npm i fastify-cli

    你还可以加入 -g 选项来全局安装它。

    接下来,在 package.json 中添加如下行:

    1. {
    2. "scripts": {
    3. "start": "fastify start server.js"
    4. }
    5. }

    然后,创建你的服务器文件:

    1. // server.js
    2. 'use strict'
    3. module.exports = async function (fastify, opts) {
    4. fastify.get('/', async (request, reply) => {
    5. return { hello: 'world' }
    6. })
    7. }

    最后,启动你的服务器:

    1. npm start