開始使用
開始使用
您好!感謝您查看 Fastify!
本文旨在溫和地介紹該框架及其功能。這是一個基礎的序言,其中包含範例和指向文件其他部分的連結。
讓我們開始吧!
安裝
使用 npm 安裝
npm i fastify
使用 yarn 安裝
yarn add fastify
您的第一個伺服器
讓我們編寫我們的第一個伺服器
// Require the framework and instantiate it
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
// Declare a route
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
// Run the server!
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
如果您在專案中使用 ECMAScript Modules (ESM),請務必在您的 package.json 中加入 "type": "module"。
{
"type": "module"
}
您偏好使用 async/await
嗎?Fastify 開箱即支援。
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
/**
* Run the server!
*/
const start = async () => {
try {
await fastify.listen({ port: 3000 })
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
太棒了,這很容易。
不幸的是,編寫複雜的應用程式需要的程式碼比這個範例多得多。當您建立新的應用程式時,一個經典的問題是如何處理多個檔案、非同步啟動以及程式碼的架構。
Fastify 提供了一個簡單的平台,可協助解決上述所有問題,甚至更多!
注意
上面的範例以及本文中的後續範例預設為僅監聽 localhost
127.0.0.1
介面。若要監聽所有可用的 IPv4 介面,應修改範例以監聽0.0.0.0
,如下所示fastify.listen({ port: 3000, host: '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 位址上的連線,並且如果作業系統支援,也接受所有 IPv4 位址上的連線。當使用
0.0.0.0
或::
部署到 Docker(或其他類型的)容器時,將會是公開應用程式的最簡單方法。
您的第一個外掛程式
與 JavaScript 中所有事物都是物件一樣,在 Fastify 中所有事物都是外掛程式。
在深入探討之前,讓我們看看它是如何運作的!
讓我們宣告我們的基本伺服器,但我們不在進入點內宣告路由,而是在外部檔案中宣告(查看路由宣告文件)。
// ESM
import Fastify from 'fastify'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = Fastify({
logger: true
})
fastify.register(firstRoute)
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
// CommonJs
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = require('fastify')({
logger: true
})
fastify.register(require('./our-first-route'))
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
// our-first-route.js
/**
* Encapsulates the routes
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://fastify.dev.org.tw/docs/latest/Reference/Plugins/#plugin-options
*/
async function routes (fastify, options) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
//ESM
export default routes;
// CommonJs
module.exports = routes
在此範例中,我們使用了 register
API,這是 Fastify 框架的核心。這是新增路由、外掛程式等等的唯一方法。
在本指南的開頭,我們指出 Fastify 提供了一個基礎,可協助應用程式的非同步啟動。為什麼這很重要?
考慮一下需要資料庫連線來處理資料儲存的情況。資料庫連線需要在伺服器接受連線之前可用。我們如何解決這個問題?
典型的解決方案是使用複雜的回呼或 promise,這是一種將框架 API 與其他程式庫和應用程式程式碼混合的系統。
Fastify 會在內部處理此問題,只需最少的精力!
讓我們用資料庫連線重寫上面的範例。
首先,安裝 fastify-plugin
和 @fastify/mongodb
npm i fastify-plugin @fastify/mongodb
server.js
// ESM
import Fastify from 'fastify'
import dbConnector from './our-db-connector.js'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = Fastify({
logger: true
})
fastify.register(dbConnector)
fastify.register(firstRoute)
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
// CommonJs
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = require('fastify')({
logger: true
})
fastify.register(require('./our-db-connector'))
fastify.register(require('./our-first-route'))
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
our-db-connector.js
// ESM
import fastifyPlugin from 'fastify-plugin'
import fastifyMongo from '@fastify/mongodb'
/**
* @param {FastifyInstance} fastify
* @param {Object} options
*/
async function dbConnector (fastify, options) {
fastify.register(fastifyMongo, {
url: 'mongodb://127.0.0.1:27017/test_database'
})
}
// Wrapping a plugin function with fastify-plugin exposes the decorators
// and hooks, declared inside the plugin to the parent scope.
export default fastifyPlugin(dbConnector)
// CommonJs
/**
* @type {import('fastify-plugin').FastifyPlugin}
*/
const fastifyPlugin = require('fastify-plugin')
/**
* Connects to a MongoDB database
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://fastify.dev.org.tw/docs/latest/Reference/Plugins/#plugin-options
*/
async function dbConnector (fastify, options) {
fastify.register(require('@fastify/mongodb'), {
url: 'mongodb://127.0.0.1:27017/test_database'
})
}
// Wrapping a plugin function with fastify-plugin exposes the decorators
// and hooks, declared inside the plugin to the parent scope.
module.exports = fastifyPlugin(dbConnector)
our-first-route.js
/**
* A plugin that provide encapsulated routes
* @param {FastifyInstance} fastify encapsulated fastify instance
* @param {Object} options plugin options, refer to https://fastify.dev.org.tw/docs/latest/Reference/Plugins/#plugin-options
*/
async function routes (fastify, options) {
const collection = fastify.mongo.db.collection('test_collection')
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
fastify.get('/animals', async (request, reply) => {
const result = await collection.find().toArray()
if (result.length === 0) {
throw new Error('No documents found')
}
return result
})
fastify.get('/animals/:animal', async (request, reply) => {
const result = await collection.findOne({ animal: request.params.animal })
if (!result) {
throw new Error('Invalid value')
}
return result
})
const animalBodyJsonSchema = {
type: 'object',
required: ['animal'],
properties: {
animal: { type: 'string' },
},
}
const schema = {
body: animalBodyJsonSchema,
}
fastify.post('/animals', { schema }, async (request, reply) => {
// we can use the `request.body` object to get the data sent by the client
const result = await collection.insertOne({ animal: request.body.animal })
return result
})
}
module.exports = routes
哇,太快了!
讓我們回顧一下我們在這裡所做的事情,因為我們引入了一些新概念。
如您所見,我們對資料庫連接器和路由註冊都使用了 register
。
這是 Fastify 的最佳功能之一,它將按照您宣告的順序載入您的外掛程式,並且僅在載入目前的外掛程式後才會載入下一個外掛程式。這樣,我們可以在第一個外掛程式中註冊資料庫連接器,並在第二個外掛程式中使用它(請閱讀這裡以了解如何處理外掛程式的作用域)。
當您呼叫 fastify.listen()
、fastify.inject()
或 fastify.ready()
時,外掛程式載入開始。
MongoDB 外掛程式使用 decorate
API 將自訂物件新增到 Fastify 實例,使其隨處可用。建議使用此 API 以方便程式碼重複使用並減少程式碼或邏輯重複。
若要深入了解 Fastify 外掛程式的運作方式、如何開發新的外掛程式以及有關如何使用整個 Fastify API 來處理非同步啟動應用程式的複雜性的詳細資訊,請閱讀外掛程式的搭便車指南。
外掛程式的載入順序
為了保證應用程式的一致性和可預測性行為,我們強烈建議始終按照如下所示的方式載入您的程式碼
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
這樣,您將始終可以存取目前作用域中宣告的所有屬性。
如先前所述,Fastify 提供了一個穩固的封裝模型,可協助您將應用程式建構成單一且獨立的服務。如果您只想為路由的子集註冊外掛程式,只需複製上述結構即可。
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
│
└── service A
│ └── plugins (from the Fastify ecosystem)
│ └── your plugins (your custom plugins)
│ └── decorators
│ └── hooks
│ └── your services
│
└── service B
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
驗證您的資料
資料驗證非常重要,也是框架的核心概念。
為了驗證傳入的請求,Fastify 使用JSON Schema。
讓我們看一個示範路由驗證的範例
/**
* @type {import('fastify').RouteShorthandOptions}
* @const
*/
const opts = {
schema: {
body: {
type: 'object',
properties: {
someKey: { type: 'string' },
someOtherKey: { type: 'number' }
}
}
}
}
fastify.post('/', opts, async (request, reply) => {
return { hello: 'world' }
})
此範例顯示如何將選項物件傳遞到路由,該物件接受一個 schema
鍵,其中包含路由、body
、querystring
、params
和 headers
的所有結構描述。
請閱讀驗證和序列化以了解更多資訊。
序列化您的資料
Fastify 對 JSON 提供一流的支援。它經過極佳的優化,可以剖析 JSON 主體並序列化 JSON 輸出。
若要加快 JSON 序列化速度(是的,它很慢!),請使用如下範例中所示的結構描述選項的 response
鍵
/**
* @type {import('fastify').RouteShorthandOptions}
* @const
*/
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, async (request, reply) => {
return { hello: 'world' }
})
透過指定如所示的結構描述,您可以將序列化速度提高 2-3 倍。這也有助於防止潛在敏感資料洩漏,因為 Fastify 只會序列化回應結構描述中存在的資料。請閱讀驗證和序列化以了解更多資訊。
剖析請求有效負載
Fastify 會原生剖析 'application/json'
和 'text/plain'
請求有效負載,結果可從 Fastify 請求物件中的 request.body
存取。
以下範例將請求的剖析主體傳回給客戶端
/**
* @type {import('fastify').RouteShorthandOptions}
*/
const opts = {}
fastify.post('/', opts, async (request, reply) => {
return request.body
})
請閱讀內容類型剖析器以了解更多關於 Fastify 預設剖析功能以及如何支援其他內容類型的資訊。
擴充您的伺服器
Fastify 的建立旨在使其具有極佳的可擴充性和最小化,我們相信僅僅一個基本的框架就足以讓出色的應用程式成為可能。
換句話說,Fastify 不是一個「內含所有功能」的框架,而是依賴一個出色的生態系統!
測試您的伺服器
Fastify 不提供測試框架,但我們建議一種撰寫測試的方法,該方法會使用 Fastify 的功能和架構。
請閱讀測試文件以了解更多資訊!
從 CLI 執行您的伺服器
多虧了 fastify-cli,Fastify 也具有 CLI 整合。
首先,安裝 fastify-cli
npm i fastify-cli
您也可以使用 -g
全域安裝它。
然後,將以下幾行新增至 package.json
{
"scripts": {
"start": "fastify start server.js"
}
}
並建立您的伺服器檔案
// server.js
'use strict'
module.exports = async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
然後使用以下命令執行您的伺服器
npm start