きっかけ
以下の記事でFastifyの説明を見て気になったので、調べてみた。
触れ込み的には、Expressの上位互換みたいな紹介だった。
Stay ahead in web development: latest news, tools, and insights #41 - DEV Community
Fastify
公式サイト
Fast and low overhead web framework, for Node.js | Fastify
Fastifyとは?
公式サイトから抜粋
効率的なサーバーは、インフラストラクチャのコストが低く、負荷がかかった状態での応答性が高く、ユーザーの満足度が高いことを意味します。セキュリティ検証と便利な開発を犠牲にすることなく、可能な限り多くのリクエストに対応しながら、サーバーのリソースを効率的に処理するにはどうすればよいでしょうか。
そこで Fastify の登場です。Fastify は、最小限のオーバーヘッドと強力なプラグイン アーキテクチャで最高の開発者エクスペリエンスを提供することに重点を置いた Web フレームワークです。Hapi と Express にヒントを得たもので、私たちが知る限り、最も高速な Web フレームワークの 1 つです。
初めて聞いたんだけど、Hapi って何?
文脈的に、webフレームワークってのは分かるけど、Expressが有名すぎて、全然知らんかった。
読み方は、ハピでいいんだよね。。。?
字面だけ見ると、アダルト方面のAPIかと思って、おじさん、ちょっとドキドキしちゃったよ
サンプル実装
いつも通り、npmプロジェクトを用意する。
そして、fastifyをインストールする。
$ npm init -y $ npm install fastify
インストールが終わったら、package.jsonのtypeにmoduleを設定しておく。
設定例は、以下の通り。※必要部分のみ抜粋
{ ・・・・ "version": "1.0.0", "main": "index.js", "type": "module", ・・・・ }
公式サイトのサンプル実装を転記。
自分は、index.jsファイルを作って転記。
// Import the framework and instantiate it import Fastify from 'fastify' const fastify = Fastify({ logger: true }) // Declare a route fastify.get('/', async function handler (request, reply) { return { hello: 'world' } }) // Run the server! try { await fastify.listen({ port: 3000 }) } catch (err) { fastify.log.error(err) process.exit(1) }
やっていることは、リクエストを受け取ったら、jsonで{ hello: 'world' }
を返す。
実行
下記で実行 ※サンプルだとserver.jsとなっているが、そこは人の好みで
$ node index.js
起動すると、下記のログが出てくる。※hostnameはマスキングしてある。
{"level":30,"time":1721431586324,"pid":7908,"hostname":"***","msg":"Server listening at http://[::1]:3000"} {"level":30,"time":1721431586325,"pid":7908,"hostname":"***","msg":"Server listening at http://127.0.0.1:3000"}
対象サーバーにアクセスすると応答が返ってくる。
自分は手軽にcurlで試したが、postmanでもブラウザアクセスでも確認できるはず。
$ curl http://localhost:3000 {"hello":"world"}
ここらへんは、expressと同じく簡単に実装できる。
サンプル実装その2(入力チェック試す)
fastifyは、入力チェックが優れているとのことなので、その入力チェックを試す。
まぁ、ゼロからやれるほど優秀でもないので、公式サイトに乗っているサンプルを試す。
実装
実装を以下に変える
// Import the framework and instantiate it import Fastify from 'fastify' const fastify = Fastify({ logger: true }) fastify.route({ method: 'GET', url: '/', schema: { // request needs to have a querystring with a `name` parameter querystring: { type: 'object', properties: { name: { type: 'string'} }, required: ['name'], }, // the response needs to be an object with an `hello` property of type 'string' response: { 200: { type: 'object', properties: { hello: { type: 'string' } } } } }, // this function is executed for every request before the handler is executed preHandler: async (request, reply) => { // E.g. check authentication }, handler: async (request, reply) => { return { hello: 'world' } } }) // Run the server! try { await fastify.listen({ port: 3000 }) } catch (err) { fastify.log.error(err) process.exit(1) }
やってることは、"/"のURLの受け付けるパラメータとして、nameを定義してある。
個人的に、このルーティングの設定を外部ファイル化したりするのだろうと思ってる。
preHandlerに、入力チェックをするのだろうと思ってる。
とりあえず、この定義で、適当なリクエストを送ると、下記が帰ってきた。
$ curl http://localhost:3000 {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"querystring must have required property 'name'"}
メッセージ的に、nameパラメータが必須だよってことだろうな。
今度は、ちゃんとnameパラメータありでリクエストを送る。
$ curl http://localhost:3000?name=test {"hello":"world"}
ちゃんと帰ってきた。
入力チェックのところは、外部のライブラリ使ったほうがいいかもな。
肝は、ルーティングをどれだけ整理して使うかだろう。
アピールポイントのまとめ
公式サイトに乗ってるアピールポイントを和訳して載せる。
- 高性能:私たちが知る限り、Fastify は最も高速な Web フレームワークの 1 つであり、コードの複雑さに応じて 1 秒あたり最大 3 万件のリクエストを処理できます。
- 拡張可能: Fastify は、フック、プラグイン、デコレータを介して完全に拡張可能です。
- スキーマ ベース:必須ではない場合でも、ルートを検証し、出力をシリアル化するためにJSON スキーマを使用することをお勧めします。Fastify は内部的に、高パフォーマンスの関数でスキーマをコンパイルします。
- ログ:ログは非常に重要ですが、コストがかかります。私たちは、このコストをほぼ排除するために最高のロガーを選択しました、Pino
- 開発者フレンドリー:このフレームワークは、パフォーマンスとセキュリティを犠牲にすることなく、表現力に優れ、開発者の日常的な使用に役立つように構築されています。
- TypeScript 対応:成長を続ける TypeScript コミュニティをサポートできるように、TypeScript型宣言ファイルの維持に努めています。
感想など
バックエンドは、だいたいSpring一択の俺ではあるが、小さい開発だったら、Fastifyも視野かなとは思う。がしかし、Expressの牙城は、そう簡単には崩せない気がする。
拡張性の高さが売りっぽいが、使いこなせるかが問題な気もする。
とりあえず試しては見たが、案外すんなり試せたので、使い勝手はいいと思う。
問題は、クラス分けがちゃんとできる人が設計できるかにかかっていると思われ。
これからの時代、ちゃんとした設計ができないと、かなり厳しい気がするんだよね。。
特に汎用性の高いライブラリは、使い方次第でゴミにも神にもなる気がしている。