SSRのNext.jsにJWT TokenをHeaderに追加してtRPC通信を行う

2023/02/10に公開

Next.jsでtRPCを初期化するときにssr: trueにすると全てのページがSSRとしてビルドされます。

export const trpc = createTRPCNext<AppRouter>({
  config({ ctx }) {
    ...
  },
  ssr: true,
});

NextJs build SSR instead of Static page when tRPC ssr is true · Discussion #958 · trpc/trpc

そのため、tRPC通信を行う場合にSSRではブラウザからCookieを取得できないことを考慮する必要があります。

クライアント側

SSRの場合はctx.req.headers.cookieでCookieを取得できるのでctx?.reqが存在している場合にのみJWT Tokenをヘッダーに追加します。

CSRの場合はブラウザから取得してヘッダーに追加します。

import type { AppRouter } from '@kontas-happy/trpc-api';
import { httpBatchLink, loggerLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import Cookies from 'js-cookie';

const getBaseUrl = () => {
  if (process.env.NEXT_PUBLIC_API_BASE_URL)
    return `${process.env.NEXT_PUBLIC_API_BASE_URL}`;
  return `http://localhost:8080`;
};

export const trpc = createTRPCNext<AppRouter>({
  config({ ctx }) {
    return {
      links: [
        loggerLink({
          enabled: (opts) =>
            process.env.NODE_ENV === 'development' ||
            (opts.direction === 'down' && opts.result instanceof Error),
        }),
        httpBatchLink({
          url: `${getBaseUrl()}/trpc`,
          headers() {
            if (ctx?.req) {
              return {
                cookie: ctx.req.headers.cookie,
              };
            }
            const sessionToken = Cookies.get('sessionToken');
            return {
              authorization: sessionToken
                ? `bearer ${sessionToken}`
                : undefined,
            };
          },
        }),
      ],
    };
  },
  ssr: true,
});

サーバー側

サーバー側ではリクエストのヘッダーにcookieauthorizationのキーでJWT Tokenが含まれているか確認します。

export const createContext = async ({
  req,
  res,
}: NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => {

  let sessionToken = '';
  if (req.headers.cookie) {
    sessionToken = req.headers.cookie.split('=')[1];
  } else if (req.headers.authorization) {
    sessionToken = req.headers.authorization.split(' ')[1];
  }
  ...

  return {
    headers: req.headers,
    req,
    res,
  };
};

Discussion