AWS-Amplify と Axios を活用した安全な API リクエスト
この記事では、Next.js 14のappルーターとAWS-Amplify Cognito認証を使用して、サーバーサイドとクライアントサイドの両方のAPIリクエストをサポートするAxiosインターセプターを作成する方法を紹介します。
まず、次のコマンドで axios インストールします。
yarn add axios
Axiosは、APIとの間のリクエストとレスポンスをインターセプトする機能があります。そのために、src/utilsディレクトリ内にaxios-interceptor.tsという名前のAxiosインターセプター用のユーティリティを作成し、すべてのリクエストとレスポンスに対してインターセプターメソッドを使用しましょう。
import axios from "axios";
const API = axios.create({
baseURL: process.env.NEXT_PUBLIC_APP_API_URL,
headers: {
'Content-Type': 'application/json',
},
});
// intercepting requests
API.interceptors.request.use(
(request) => {
// handle request here
},
(error: AxiosError) => {
//handle error here
}
);
//intercepting responses
API.interceptors.response.use(
(response) => {
// handle response here
},
(error: AxiosError) => {
//handle error here
}
);
export {API};
Axiosインターセプターの使用例の1つは、認証目的で各リクエストのヘッダーにトークンを追加することで、セキュアなネットワークリクエストを行うことです。これを実現するには、リクエストヘッダーに認証トークンを添付するリクエストハンドラーを追加するだけです。
const requestHandler = async (request: InternalAxiosRequestConfig) => {
request.headers.Authorization = "XXXXXXXXXXXXXX";
return request;
};
AWS-Amplifyは、認証に人気のあるAmazon Cognitoサービスを利用するための様々なメソッドを提供しています。適切に設定すれば、サーバーサイドとクライアントサイドの両方で実行できる認証メソッドを提供します。例えば、認証されたサーバーリクエストが必要な場合、Amplifyをサーバーで設定してリクエストを行うことができ、クライアントサイドでも同様です。
例えば、リクエストを実行するユーザーのidTokenを認証ヘッダーに送信する必要がある場合、まずidTokenを取得する必要があります:
クライアントサイドリクエストの場合
import { fetchAuthSession } from "aws-amplify/auth";
const requestHandler = async (request: InternalAxiosRequestConfig) => {
try {
const { idToken } = (await fetchAuthSession()).tokens ?? {};
if (idToken) {
request.headers.Authorization = `Bearer ${idToken.toString()}`;
}
} catch (_) {
// handle error
}
return request;
};
サーバーサイドリクエストの場合、ユーザーの認証セッションを取得するために少し工夫が必要です。(aws-amplifyサーバーメソッドの設定と実装についてはこちらの記事を参照してください)
import { fetchAuthSession } from "aws-amplify/auth/server";
import { cookies } from "next/headers";
import { runWithAmplifyServerContext } from "./amplify-server-utils";
export const fetchAuthSessionFromServer = async () => {
const currentSession = await runWithAmplifyServerContext({
nextServerContext: { cookies },
operation: (contextSpec) => fetchAuthSession(contextSpec),
});
return currentSession;
};
const requestHandler = async (request: InternalAxiosRequestConfig) => {
try {
const { idToken } = (await fetchAuthSessionServer()).tokens ?? {};
if (idToken) {
request.headers.Authorization = `Bearer ${idToken.toString()}`;
}
} catch (_) {
//handle error
}
return request;
};
しかし、リクエストがサーバーサイドかクライアントサイドかをどのように判断すればよいでしょうか?
この問題に対する簡単な解決策は、リクエストがクライアントサイドかサーバーサイドかを判断するカスタムヘッダーを渡すことです。
これで、リクエストハンドラーを次のように設定できます:
const requestHandler = async (request: InternalAxiosRequestConfig) => {
const useServer: boolean = request.headers["X-Use-Server"];
try {
const { idToken } =
(await (useServer ? fetchAuthSessionFromServer() : fetchAuthSession()))
.tokens ?? {};
if (idToken) {
request.headers.Authorization = `Bearer ${idToken.toString()}`;
}
} catch (_) {
//handle error
}
return request;
};
使用方法
Next.jsでは、認証されたユーザーの初期データを取得するサーバーページで、Axiosを次のように使用できます:
// cart/page.tsx
const CartPage = async () => {
const cart: Cart = await API.get("/cart", {
headers: {
"X-Use-Server": true,
},
});
return (
<div className="flex items-center justify-center min-h-screen">
{cart.items.length}
{cart.totalPrice}
</div>
);
};
export default CartPage;
クライアントコンポーネントでAxiosを使用するには、カスタムヘッダーにfalse値を渡すか、リクエストにヘッダーを送信しないだけです。
結論
このように、クライアントとサーバーの両方のコンポーネントでAPIリクエストを行うために、Axiosとインターセプターを設定することができます。ただし、これはNext.jsアプリでAxiosを使用してリクエストを実行する様々な方法の1つに過ぎません。別々のAPIクライアント(インターセプター)を作成することもできます。また、Next.jsのネイティブfetchメソッドを型安全なAPIクライアントユーティリティとして作成し、必要に応じて再利用することもできます。
参考文献:
この記事は、2024年5月に弊社のエンジニア Manoj Pudasaini が執筆した内容を日本語に翻訳したものです。
英語版はこちらをご覧ください。
https://articles.wesionary.team/configuring-axios-interceptors-for-secure-server-side-and-client-side-api-requests-with-aws-amplify-5d3467649041
採用情報
私たちはプロダクト共創の仕組み化に取り組んでいます。プロダクト共創をリードするプロダクト・マネージャー、そして、私たちのビジョンを市場に届ける営業メンバーを募集しています!
開発パートナーをお探しの企業様へ
弊社は、グローバル開発のメリットを活かし、高い費用対効果と品質を両立しています。経験豊富で多様性のあるチームが、課題を正しく理解し、最適なシステムと優れた体験を実現します。業務システムの開発、新規事業の開発、業務効率化やDX化に関するお困りごと、ぜひ弊社にご相談ください。