Fastify 入門

2020-02-06 15:35 更新

Fastify

起步

Hello!感謝你來到 Fastify 的世界!這篇文檔將向你介紹 Fastify 框架及其特性,也包含了一些示例和指向其他文檔的鏈接。那,這就開始吧!

安裝

使用 npm 安裝:

npm i fastify --save

使用 yarn 安裝:

yarn add fastify

第一個服務(wù)器

讓我們開始編寫第一個服務(wù)器吧:

// 加載框架并新建實例
const fastify = require('fastify')({
  logger: true
})

// 聲明路由
fastify.get('/', function (request, reply) {
  reply.send({ hello: 'world' })
})

// 啟動服務(wù)!
fastify.listen(3000, function (err, address) {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`server listening on ${address}`)
})

更喜歡使用 async/await?Fastify 對其提供了開箱即用的支持。(我們還建議使用 make-promises-safe 來避免文件描述符 (file descriptor) 及內(nèi)存的泄露)

const fastify = require('fastify')()

fastify.get('/', async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000)
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

如此簡單,棒極了!可是,一個復(fù)雜的應(yīng)用需要比上例多得多的代碼。當你從頭開始構(gòu)建一個應(yīng)用時,會遇到一些典型的問題,如多個文件的操作、異步引導(dǎo),以及代碼結(jié)構(gòu)的布置。幸運的是,F(xiàn)astify 提供了一個易于使用的平臺,它能幫助你解決不限于上述的諸多問題!

注本文檔中的示例,默認情況下只監(jiān)聽本地 127.0.0.1 端口。要監(jiān)聽所有有效的 IPv4 端口,需要將代碼修改為監(jiān)聽 0.0.0.0,如下所示:fastify.listen(3000, '0.0.0.0', function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } fastify.log.info(`server listening on ${address}`) })類似地,::1 表示只允許本地的 IPv6 連接。而 :: 表示允許所有 IPv6 地址的接入,當操作系統(tǒng)支持時,所有的 IPv4 地址也會被允許。當使用 Docker 或其他容器部署時,這會是最簡單的暴露應(yīng)用的方式。

第一個插件

就如同在 JavaScript 中一切皆為對象,在 Fastify 中,一切都是插件 (plugin)。在深入之前,先來看看插件系統(tǒng)是如何工作的吧!讓我們新建一個基本的服務(wù)器,但這回我們把路由 (route) 的聲明從入口文件轉(zhuǎn)移到一個外部文件。(參閱路由聲明)。

const fastify = require('fastify')({
  logger: true
})

fastify.register(require('./our-first-route'))

fastify.listen(3000, function (err, address) {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`server listening on ${address}`)
})
// our-first-route.js

async function routes (fastify, options) {
  fastify.get('/', async (request, reply) => {
    return { hello: 'world' }
  })
}

module.exports = routes

這個例子調(diào)用了 register API,它是 Fastify 框架的核心,也是添加路由、插件等的唯一方法。

在本文的開頭,我們提到 Fastify 提供了幫助應(yīng)用異步引導(dǎo)的基礎(chǔ)功能。為什么這一功能十分重要呢? 考慮一下,當存在數(shù)據(jù)庫操作時,數(shù)據(jù)庫連接顯然要在服務(wù)器接受外部請求之前完成。該如何解決這一問題呢?典型的解決方案是使用復(fù)雜的回調(diào)函數(shù)或 Promise,但如此會造成框架的 API、其他庫以及應(yīng)用程序的代碼混雜在一起。Fastify 則不走尋常路,它從本質(zhì)上用最輕松的方式解決這一問題!

讓我們重寫上述示例,加入一個數(shù)據(jù)庫連接。(在這里我們用簡單的例子來說明,對于健壯的方案請考慮使用 fastify-mongo 或 Fastify 生態(tài)中的其他插件)

server.js

const fastify = require('fastify')({
  logger: true
})

fastify.register(require('./our-db-connector'), {
  url: 'mongodb://localhost:27017/'
})
fastify.register(require('./our-first-route'))

fastify.listen(3000, function (err, address) {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`server listening on ${address}`)
})

our-db-connector.js

const fastifyPlugin = require('fastify-plugin')
const MongoClient = require('mongodb').MongoClient

async function dbConnector (fastify, options) {
  const url = options.url
  delete options.url

  const db = await MongoClient.connect(url, options)
  fastify.decorate('mongo', db)
}
// 用 fastify-plugin 包裝插件,以使插件中聲明的裝飾器、鉤子函數(shù)及中間件暴露在根作用域里。
module.exports = fastifyPlugin(dbConnector)

our-first-route.js

async function routes (fastify, options) {
  const database = fastify.mongo.db('db')
  const collection = database.collection('test')

  fastify.get('/', async (request, reply) => {
    return { hello: 'world' }
  })

  fastify.get('/search/:id', async (request, reply) => {
    const result = await collection.findOne({ id: request.params.id })
    if (result.value === null) {
      throw new Error('Invalid value')
    }
    return result.value
  })
}

module.exports = routes

哇,真是快??!介紹了一些新概念后,讓我們回顧一下迄今為止都做了些什么吧。如你所見,我們可以使用 register 來注冊數(shù)據(jù)庫連接器或者路由。 這是 Fastify 最棒的特性之一了!它使得插件按聲明的順序來加載,唯有當前插件加載完畢后,才會加載下一個插件。如此,我們便可以在第一個插件中注冊數(shù)據(jù)庫連接器,并在第二個插件中使用它。(參見 這里 了解如何處理插件的作用域)。 當調(diào)用函數(shù) fastify.listen()、fastify.inject() 或 fastify.ready() 時,插件便開始加載了。

我們還用到了 decorate API?,F(xiàn)在來看看這一 API 是什么,以及它是如何運作的吧。 考慮下需要在應(yīng)用的不同部分使用相同的代碼或庫的場景。一種解決方案便是按需引入這些代碼或庫。哈,這固然可行,但卻因為重復(fù)的代碼和麻煩的重構(gòu)讓人苦惱。為了解決上述問題,F(xiàn)astify 提供了 decorate API。它允許你在 Fastify 的命名空間下添加自定義對象,如此一來,你就可以在所有地方直接使用這些對象了。

更深入的內(nèi)容,例如插件如何運作、如何新建,以及使用 Fastify 全部的 API 去處理復(fù)雜的異步引導(dǎo)的細節(jié),請看插件指南

插件加載順序

為了保證應(yīng)用的行為一致且可預(yù)測,我們強烈建議你采用以下的順序來組織代碼:

└── 來自 Fastify 生態(tài)的插件
└── 你自己的插件
└── 裝飾器
└── 鉤子函數(shù)和中間件
└── 你的服務(wù)應(yīng)用

這確保了你總能訪問當前作用域下聲明的所有屬性。如前文所述,F(xiàn)astify 提供了一個可靠的封裝模型,它能幫助你的應(yīng)用成為單一且獨立的服務(wù)。假如你要為某些路由單獨地注冊插件,只需復(fù)寫上述的結(jié)構(gòu)就足夠了。

└── 來自 Fastify 生態(tài)的插件
└── 你自己的插件
└── 裝飾器
└── 鉤子函數(shù)和中間件
└── 你的服務(wù)應(yīng)用
    │
    └──  服務(wù) A
    │     └── 來自 Fastify 生態(tài)的插件
    │     └── 你自己的插件
    │     └── 裝飾器
    │     └── 鉤子函數(shù)和中間件
    │     └── 你的服務(wù)應(yīng)用
    │
    └──  服務(wù) B
    │     └── 來自 Fastify 生態(tài)的插件
    │     └── 你自己的插件
    │     └── 裝飾器
    │     └── 鉤子函數(shù)和中間件
    │     └── 你的服務(wù)應(yīng)用

驗證數(shù)據(jù)

數(shù)據(jù)的驗證在我們的框架中是極為重要的一環(huán),也是核心的概念。Fastify 使用 JSON Schema 驗證來訪的請求。 讓我們來看一個驗證路由的例子:

const opts = {
  schema: {
    body: {
      type: 'object',
      properties: {
        someKey: { type: 'string' },
        someOtherKey: { type: 'number' }
      }
    }
  }
}

fastify.post('/', opts, async (request, reply) => {
  return { hello: 'world' }
})

這個例子展示了如何向路由傳遞配置選項。選項中包含了一個名為 schema 的對象,它便是我們驗證路由所用的模式 (schema)。借由 schema,我們可以驗證 body、querystring、params 以及 header。請參閱驗證與序列化獲取更多信息。

序列化數(shù)據(jù)

Fastify 對 JSON 提供了優(yōu)異的支持,極大地優(yōu)化了解析 JSON body 與序列化 JSON 輸出的過程。在 schema 的選項中設(shè)置 response 的值,能夠加快 JSON 的序列化 (沒錯,這很慢!),就像這樣:

const opts = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

fastify.get('/', opts, async (request, reply) => {
  return { hello: 'world' }
})

簡單地指明 schema,序列化的過程就達到了原先 2-3 倍的速度。這么做同時也保護了潛在的敏感數(shù)據(jù)不被泄露,因為 Fastify 僅對 schema 里出現(xiàn)的數(shù)據(jù)進行序列化。 請參閱 驗證與序列化獲取更多信息。

擴展服務(wù)器

Fastify 生來十分精簡,也具有高可擴展性。我們相信,一個小巧的框架足以實現(xiàn)一個優(yōu)秀的應(yīng)用。換句話說,F(xiàn)astify 并非一個面面俱到的框架,它依賴于自己驚人的生態(tài)系統(tǒng)!

測試服務(wù)器

Fastify 并沒有提供測試框架,但是我們推薦你在測試中使用 Fastify 的特性及結(jié)構(gòu)。更多內(nèi)容請看測試!

從命令行啟動服務(wù)器

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

首先,你得安裝 fastify-cli:

npm i fastify-cli

你還可以加入 -g 選項來全局安裝它。

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

{
  "scripts": {
    "start": "fastify start server.js"
  }
}

然后,創(chuàng)建你的服務(wù)器文件:

// server.js
'use strict'

module.exports = async function (fastify, opts) {
  fastify.get('/', async (request, reply) => {
    return { hello: 'world' }
  })
}

最后,啟動你的服務(wù)器:

npm start

幻燈片與視頻 (英文資源)


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號