AWS Lambda (Node.js) でのHTTPリクエストをAxiosからFetch APIに移行する
こんにちは。システム開発部のkiwiです。
AWS LambdaのNode.js 16ランタイムのサポート終了が2024年6月12日と迫ってきました(公式ドキュメント)。弊社でも対象の関数についてNode.js 18以上のランタイムへの移行を進めており、ランタイムの変更のほか、新機能を使ってより簡潔な記述や効率の良い処理ができないか検討しています。その一つとして、LambdaからHTTPリクエストする際の処理についても見直しを行ないました。
Lambdaから別のAPIなどへHTTPリクエストする場合、ニフティ不動産では現在Axiosというライブラリを主に使用していますが、Node.js 18でFetch APIがexperimentalフラグなしで利用できるようになり、ライブラリを使わなくても簡潔にリクエストが書けるようになりました。
Fetch APIを利用することで、ライブラリの読み込み時間の短縮や依存関係の削減を見込むことができます。今回はAPIへリクエストする処理を例に、書き換える際のポイントをまとめます。
目次
前提
Node.jsにおけるFetch APIは、Node.js 18時点ではまだ実験的機能として実装されており、プロダクション環境での採用には不向きです。この記事の内容は検証環境で試したものを記載しており、プロダクション環境には反映しておりません。
なお、Fetch APIはNode.js 21で安定版となりました。そのため、LambdaでもNode.js 22ランタイムから安定版が利用可能です(2024年11月予定)。
全体像
外部APIへPOSTで接続を行う処理の、置き換え前(Axios利用)のコードは以下のとおりです。
const ENDPOINT = 'https://example.com/api';
const API_KEY = 'xxxxxxxxxsecret';
const REQUEST_TIMEOUT_MS = 5000;
try {
const response = await axios.post(ENDPOINT, {
hoge: "fuga"
}, {
url: ENDPOINT,
headers: { 'x-api-key': API_KEY },
timeout: REQUEST_TIMEOUT_MS
});
return response.data;
} catch (e) {
console.error(e);
if (e.response) {
return e.response.data;
}
}
これに対して、同じ処理を置き換えた(Fetch API)コードが以下です。
const ENDPOINT = 'https://example.com/api';
const API_KEY = 'xxxxxxxxxsecret';
const REQUEST_TIMEOUT_MS = 5000;
try {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': API_KEY
},
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
body: JSON.stringify({
hoge: "fuga"
})
});
if (!response.ok) {
console.error(`response error`);
}
return await response.json();
} catch (e) {
console.error(e);
}
置き換えのポイント
上記のコード例を元に、ポイントを解説していきます。
レスポンスの取り扱い
Axiosでは、JSON形式で返却された場合は response.data
にアクセスすれば、JSONパース後のオブジェクトを受け取ることができました。
Fetch APIの場合、レスポンスのbodyをJSONで受け取る場合 await response.json()
メソッドを呼び出してパースする必要があります。bodyがテキストデータの場合は await response.text()
を、画像などのバイナリデータの場合は await response.blob()
を実行します。いずれもPromiseが返却されるため、awaitコマンドをつけたり .then()
で繋げたりする必要があるため注意が必要です。
エラーレスポンスの取り扱い
Axiosでは、全てのエラーをcatch
で処理するため、エラーレスポンスを受け取った場合はエラー(reject)となります。エラーレスポンスによるエラーの場合、error.response
でレスポンス内容を取得できるため、リクエスト時のエラーと分けて処理をする場合はこの項目の有無を確認します。
Fetch APIでは、エラーレスポンスを受け取った場合もPromiseがresolveされるため、catch
には入りません。 response.ok
プロパティでリクエストが成功(ステータスコードが200〜299)したかどうかわかるため、レスポンスをもとに必要なエラーハンドリングを行います。
タイムアウトの設定
Axiosでは、タイムアウトの設定を行う場合はオプションとして timeout
を設定することで実現可能でした。
Fetch APIには同様の設定はありませんが、リクエストのキャンセルを行うための AbortSignal (AbortController.signal)
を指定することが可能です。そのため、ほかのキャンセル手段が必要ない場合は AbortSignal.timeout(REQUEST_TIMEOUT_MS)
という指定でタイムアウトを設定可能です。
なお、Axiosでもv0.22.0よりAbortController.signal
を指定することで、独自実装のCancelToken
を使わずにキャンセル機能が実装可能です。
GETリクエストのパラメーター指定
Axiosでは、GETリクエストを行う場合、URL文字列にパラメータを含める方法のほか、オブジェクトを params
に渡すことでリクエストを構築可能でした。
const response = await axios.get('/user', {
params: {
ID: 12345
}
})
Fetch APIではこの指定方法が使えません。ただしエンドポイントとして URL
形式を利用できるため、 URLSearchParams
を使ってURLを構築することが可能です。
const endpoint = new URL(ENDPOINT);
const params = new URLSearchParams();
params.append("ID", 12345);
endpoint.search = params.toString();
const response = await fetch(endpoint);
まとめ
こうして置き換えてみると、Fetch APIはリソース取得に関するより汎用的な仕組みとして実装されていることがわかります。AxiosはAPI通信でよくある処理をオプション指定のみで実装可能となっており、Axiosを使い続けるメリットも見えてきますね。
Lambdaランタイムの更新は処理内容を見直す良い機会だと思います。例えばNode.js 18では配列に関するメソッド findLast()
, findLastIndex()
が、Node.js 19ではロケールに合わせて数値書式を設定する Intl.NumberFormat
が追加されるなど、より効率よく実装するための機能も増えています。
運用作業として、ランタイムの更新と動作確認のみに留めることも多いと思いますが、余裕があればぜひ処理の見直しも実施してみてはいかがでしょうか。
最後に、繰り返しになりますが、Node.jsにおけるFetch APIは、Node.js 18時点では実験的機能となっています。プロダクション環境へ反映する際は十分に考慮してください。
掲載内容は、記事執筆時点の情報をもとにしています。