Apollo Client や URQL でリクエストヘッダーやレスポンスヘッダーを操作する
投稿日: 2024/06/25
GraphQLのクライエントパッケージとしては Apollo Client や URQL が有名です。
リクエストヘッダーに値をセットしたり、レスポンスヘッダーから値を取得するコードを書いたので記事にしておきます。
今回は例としてCSRFトークンをセットしたり、取り出したりします。
書いてるコードはTypescriptなのでJavascriptで書く人はうまく読み替えてください。
登場するRouterコンポーネントはReactRouterを使ったものですが、別になんでもいいです。
また、説明中に出てくるsessionStorageはブラウザにデータを保存するものです。サーバーサイドで扱うセッションやCookieではないです。
https://developer.mozilla.org/ja/docs/Web/API/Window/sessionStorage
Apollo Client の例
import { ApolloClient, InMemoryCache, ApolloProvider, HttpLink, ApolloLink, } from "@apollo/client"; import Router from "./Router"; const App: React.FC = () => { const httpLink = new HttpLink({ uri: "/graphql" }); const middlewareLink = new ApolloLink((operation, forward) => { operation.setContext(({ headers = {} }) => ({ headers: { ...headers, "X-CSRF-Token": sessionStorage.getItem("csrfToken") || document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]') ?.content || null, }, })); return forward(operation); }); const afterwareLink = new ApolloLink((operation, forward) => { return forward(operation).map((response) => { const context = operation.getContext(); const headers = context.response.headers; if (headers && headers.get("csrf-token")) { sessionStorage.setItem("csrfToken", headers.get("csrf-token")); } return response; }); }); const client = new ApolloClient({ link: ApolloLink.from([middlewareLink, afterwareLink, httpLink]), cache: new InMemoryCache(), }); return ( <ApolloProvider client={client}> <Router /> </ApolloProvider> ); }; export default App;
middlewareLinkの方がリクエストヘッダーにCSRFトークンをセットしています。
sessionStorageにトークンが入っていればそれを使い、なければHTMLのhead内metaタグ内に入ってるトークンを使うようにしてます。
afterwareLinkの方がレスポンスヘッダーからCSRFトークンを取り出しています。
レスポンスヘッダーにトークンが存在すればsessionStorageに保存してます。
URQL
import { createClient, Provider } from "urql"; import Router from "./Router"; const App: React.FC = () => { const client = createClient({ url: "/graphql", fetchOptions: () => { const csrfToken = sessionStorage.getItem("csrfToken") || document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]') ?.content || ""; return { headers: { "X-CSRF-Token": csrfToken, }, }; }, fetch: (...args) => fetch(...args).then((response) => { const csrfToken = response.headers.get("csrf-token"); if (csrfToken) { sessionStorage.setItem("csrfToken", csrfToken); } return response; }), }); return ( <Provider value={client}> <Router /> </Provider> ); }; export default App;
fetchOptionsオプションを使ってリクエストヘッダーに値をセットできます。
公式ドキュメントにあるようにheaders: { ... }
のハッシュを返す処理を書けばよいので、ハッシュの中にCSRFトークンを入れてます。
fetchオプションを使ってレスポンスヘッダーから値を取得できます。
Apollo clientの時と同様に、レスポンスヘッダー内にトークンがあればsessionStorageに保存しています。
参考
Noh を作ってるエンジニア。