Fastifyで内部APIと外部APIを実行する方法
Fastify.jsにおけるAPI連携の例を解説します。
概要
Fastify.jsは、Node.jsの軽量で高性能なWebアプリケーションフレームワークで、APIとの連携によく使用されます。APIとの連携を実現するためには、リクエストの送信やレスポンスの処理が必要となります。
ここでは、以下バージョンを使用した、Fastify.jsでAPIとの連携を行う方法を説明します。
Fastify.js v4.0.0
nodejs v19.7.0
また、今回作成するコードは全て、GitHubに掲載しています。
もちろんです。フロントエンドからAPIを実行する例として、JavaScriptを使用してFetch APIを使う方法について追記します。
フロントエンドからAPIを実行する方法
フロントエンドでAPIを使用することは一般的なシナリオで、JavaScriptのFetch APIを使うと簡単に実装できます。Fetch APIはブラウザがネイティブで提供するAPIで、非同期HTTP(Ajax)リクエストを行うことができます。
サーバー側の実装する前に、@fastify/staticプラグインを使用するため、以下コマンドを実行してインストールします。
npm install @fastify/static@latest
以下の例では、Fastify.jsで作成した/users
エンドポイントにPOSTリクエストを送信し、新しいユーザーを作成します。
server.js
// モジュールをインポートします
const fastify = require("fastify")({ logger: true });
const path = require("path");
const fs = require("fs");
// ユーザーを保存するための仮のデータストア
let users = [];
fastify.register(require("@fastify/static"), {
root: path.join(__dirname, "views"),
prefix: "/", // optional: default '/'
});
fastify.get("/users", (req, reply) => {
// ユーザーデータをレスポンスとして返す
reply.send(users);
});
fastify.post("/users", (req, reply) => {
// リクエストボディからユーザーデータを取得
const user = req.body;
// ユーザーをデータストアに追加
users.push(user);
// 新しく作成されたユーザーをレスポンスとして返す
reply.send(user);
});
fastify.listen({ port: 3000 }, (err) => {
if (err) {
console.error("サーバーの起動中にエラーが発生しました:", err);
process.exit(1);
}
console.log("サーバーがポート3000で起動しました");
});
解説
このコードは、ユーザーの一覧と作成をサポートする基本的なFastifyサーバーを構築します。 以下に、それぞれの部分の詳細な説明を示します。
- モジュールをインポート
const fastify = require("fastify")({ logger: true });
const path = require("path");
この部分では、fastify
とpath
という2つのモジュールをインポートします。
- HTMLファイルをホストするディレクトリを指定
fastify.register(require('@fastify/static'), {
root: path.join(__dirname, 'views'),
prefix: '/', // optional: default '/'
})
ここでは、@fastify/static
プラグインを使って、“views"というディレクトリの静的ファイルをホストします。
- ユーザーデータを保存するための仮データストア
let users = [];
この部分では、作成されたユーザーを保存するための配列を定義します。
- ルーティング
fastify.get("/", (req, reply) => {
reply.sendFile('index.html') // serving path.join(__dirname, 'views', 'index.html') directly
});
fastify.get()
メソッドを使って、ルートURL(”/")へのGETリクエストをハンドリングします。リクエストが来ると、“index.html"ファイルをレスポンスとして返します。
- ユーザーデータの取得
fastify.get("/users", (req, reply) => {
reply.send(users);
});
/users
へのGETリクエストが来たときに、保存されている全ユーザーの一覧をJSON形式でレスポンスとして返します。
- ユーザーの作成
fastify.post("/users", (req, reply) => {
const user = req.body;
users.push(user);
reply.send(user);
});
/users
へのPOSTリクエストが来たときに、リクエストボディからユーザーデータを取得し、それをデータストアに追加します。新しく作成されたユーザーをJSON形式でレスポンスとして返します。
- サーバーの起動
fastify.listen(3000, (err, address) => {
if (err) throw err
fastify.log.info(`server listening on ${address}`)
})
最後に、サーバーを起動し、ポート3000でリッスンします。サーバーが起動すると、“server listening on {address}“というメッセージがコンソールに表示されます。
index.html
<!DOCTYPE html>
<html>
<body>
<!-- ボタンを追加 -->
<button onclick="createUser()">Create User</button>
<script>
async function createUser() {
const response = await fetch('http://localhost:3000/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'John', age: 25 })
});
if (!response.ok) {
const message = await response.text();
throw new Error(`An error has occurred: ${message}`);
}
const user = await response.json();
console.log(user);
}
</script>
</body>
</html>
解説
このソースコードは、ユーザーを作成するためのHTMLページを生成します。
- HTMLドキュメントの定義とボタンの追加:
<!DOCTYPE html>
<html>
<body>
<!-- ボタンを追加 -->
<button onclick="createUser()">Create User</button>
この部分では、HTML文書の基本的な構造を定義し、ユーザーを作成するためのボタンを追加しています。ボタンがクリックされるとcreateUser
関数が実行されます。
createUser
関数の定義:
<script>
async function createUser() {
const response = await fetch('http://localhost:3000/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'John', age: 25 })
});
この部分で非同期関数createUser
を定義しています。この関数は、fetch
APIを使用して、指定したURL(この場合はhttp://localhost:3000/users
)に対してPOSTリクエストを送信します。リクエストボディには、作成するユーザーの情報をJSON形式で指定します。
- エラーハンドリング:
if (!response.ok) {
const message = await response.text();
throw new Error(`An error has occurred: ${message}`);
}
response.ok
プロパティは、レスポンスが成功したかどうか(HTTPステータスコードが200~299の範囲かどうか)を判断します。成功していない場合、エラーメッセージを生成してエラーをスローします。
- レスポンスの処理とログ出力:
const user = await response.json();
response.json()
メソッドを使用して、レスポンスボディをJSONとして解析します。
最終的に以下のようなディレクトリ構成になります。
integration
├── node_modules
│ └── (依存関係のパッケージ)
├── views
│ └── index.html
├── server.js
├── package.json
└── package-lock.json
外部APIへのリクエストを送信する
外部APIへのリクエストを送信するためには、通常、HTTPクライアントライブラリが使用されます。ここでは、その一つであるaxios
を使用します。まず、npmを使ってaxios
をインストールします。
npm install axios
const fastify = require("fastify")({ logger: true });
const axios = require("axios");
fastify.get("/data", async (request, reply) => {
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
reply.send(response.data);
} catch (error) {
reply.status(500).send({ message: "Error occurred" });
}
});
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err;
fastify.log.info(`server listening on ${address}`);
});
解説
このコードは、JSONプレースホルダーAPIからデータを取得し、そのデータをレスポンスとして送信するFastifyサーバーを作成します。
- モジュールのインポート:
const fastify = require('fastify')({ logger: true });
const axios = require('axios');
この部分で、fastify
とaxios
という2つのモジュールをインポートします。fastify
はWebサーバーフレームワーク、axios
はHTTPクライアントライブラリです。
- ルートの定義:
fastify.get('/data', async (request, reply) => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
reply.send(response.data);
} catch (error) {
reply.status(500).send({ message: 'Error occurred' });
}
});
/data
エンドポイントにGETリクエストが来たときのハンドラーを定義します。非同期関数内でaxios.get
を使って外部APIからデータを取得し、そのデータをレスポンスとして送信します。エラーが発生した場合は、ステータスコード500とエラーメッセージを含むレスポンスを送信します。
- サーバーの起動:
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err;
fastify.log.info(`server listening on ${address}`);
});
最後に、サーバーを起動し、ポート3000でリッスンします。サーバーが起動すると、“server listening on {address}“というメッセージがコンソールに表示されます。
テスト
curl http://localhost:3000/data
CURLコマンドを使用して、各エンドポイントの動作をテストすることができます。ただし、アプリケーションがポート3000で実行されていることを確認してください。また、必要に応じてホスト名やポート番号を変更してください。
まとめ
この記事では、Fastifyを使用してAPIを実行する方法を学びました。Fastifyは、Node.jsでAPIを構築するための高速で効率的なフレームワークです。Fastifyは、ExpressやKoaなどの他のフレームワークと比較しても、高速であることが知られています。Fastifyは、Node.jsのAPI開発において、高速で効率的なフレームワークとして人気を博しています。