メインコンテンツにスキップ
バージョン: 最新 (v5.0.x)

スタートガイド

はじめに

こんにちは! 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は、上記のすべての問題などを解決するのに役立つ簡単なプラットフォームを提供します!

注記

上記の例、およびこのドキュメントの以降の例では、デフォルトでローカルホストの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}`)
})

同様に、IPv6経由でローカル接続のみを受け入れるには、::1を指定します。または、すべての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.dokyumento.jp/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://localhost: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.dokyumento.jp/docs/latest/Reference/Plugins/#plugin-options
*/
async function dbConnector (fastify, options) {
fastify.register(require('@fastify/mongodb'), {
url: 'mongodb://localhost: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.dokyumento.jp/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の最高の機能の1つです。プラグインを宣言したのと同じ順序でロードし、現在のプラグインがロードされた後にのみ次のプラグインをロードします。このように、最初のプラグインにデータベースコネクタを登録し、2番目のプラグインでそれを使用できます*(プラグインのスコープの処理方法についてはこちらをお読みください)*。

プラグインのロードは、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' }
})

この例は、ルート、bodyquerystringparams、およびheadersのすべてのスキーマを含むschemaキーを受け入れるオプションオブジェクトをルートに渡す方法を示しています。

詳細については、検証とシリアル化をお読みください。

データのシリアル化

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は、fastify-cliのおかげで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

スライドとビデオ