🐾

Apollo Client や URQL でリクエストヘッダーやレスポンスヘッダーを操作する

2022/07/29に公開

この記事は以下のページに移動しました。

https://noh.ink/articles/oyI7tWGjqCmyhfzQowVq





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に保存しています。

参考

https://formidable.com/open-source/urql/docs/basics/react-preact/#setting-up-the-client

https://stackoverflow.com/questions/61388188/urql-graphql-how-can-i-get-response-headers

Discussion