Next.jsのSSRでJWTをHeaderに埋め込む方法

##概要
Next.jsではCSR, SSR, SSG, ISRといった色々なレンダリング方法を使い分けることが可能です。

SSGやISRはビルド時にhtmlが生成されるためパフォーマンスが最も高く、Next.jsの開発元であるVercelも推奨しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246985/a1382264-a99d-487e-24e3-431e9140a5e5.png)


ただビルド時だとユーザーのリクエストを受け取れないため、例えばログイン状態でAPIを叩いてレンダリングさせたい場合などはSSGでなくSSRを使ってPre-renderingするのがいいかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246985/7cad3165-e08d-3612-b340-d48df0e47906.png)

今回はSSRで実際どのように認証情報(JWT)を乗せてリクエストを送るのか、Apollo Clientとaxiosの2パターンを紹介しようと思います

参考
[SSG と SSR で理解する Next.js のページレンダリング](https://zenn.dev/luvmini511/articles/1523113e0dec58)

##前提
JWTをサーバー側に渡すにはcookieを使います
今回は[nookies](https://www.npmjs.com/package/nookies)というライブラリを使います
ログイン処理にて `session` という名前でトークンを保存しておきます

```page/login.tsx
const emailLogin = async () => {
    const token = await user?.getIdToken();

    const options = {
      maxAage: 60 * 60,
      secure: true,
      path: '/',
    }

    if (token) setCookie({ res }, 'session', token, options);    
};
```

##Apollo Client
```page/index.tsx
export default function Home({
  postData
}) {
  /* 省略 */
}

export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {
    const cookies = nookies.get(context);
    const accessToken = cookies.session;
    const apolloClient = initializeApollo(null, context); //apolloClient初期化

    // トークンがない場合ログイン画面にリダイレクト
    if (!accessToken) {
      return {
        redirect: {
          permanent: false,
          destination: '/login',
        },

        props: {} as never,
      };
    }

    const { data: postData } = await apolloClient.query({
      query: GetPostDataDocument,
    });

    return {
      props: {
        postData
      },
    };
}
```

```apolloClient.ts
let apolloClient: ApolloClient<NormalizedCacheObject>;

function createApolloClient(ctx: { req: any }) {
  const httpLink = createHttpLink({
    uri: 'https://api.service.com/',
  });

  const authLink = setContext((req, { headers }) => {
    const accessToken = nookies.get(ctx).session; // JWT取り出し
    return {
      headers: {
        ...headers,
        someoneToken: accessToken || '', // Headerに埋め込み
      },
    };
  });

  return new ApolloClient({
    ssrMode: typeof window === 'undefined',
    link: authLink.concat(httpLink),
    cache: new InMemoryCache(),
    credentials: 'include',
  });
}

export function initializeApollo(initialState = null, ctx) {
  const _apolloClient = apolloClient ?? createApolloClient(ctx); //このctxにcookie情報が含まれる

  if (initialState) {
    _apolloClient.cache.restore(initialState);
  }

  return _apolloClient;
}
```

###ポイント
getServerSideProps は、SSRのときにサーバーサイドでのみ実行されるAPIとなります。

そちらで受け取る context パラメータには Node HTTPServer の req、 res などが内包しており、クライアント側の cookie 情報を取得できます。 

なので context を apollo Client の初期化時に引数で渡して、Header に格納するという流れになります。


##Axios
```page/index.tsx

/* クライアント側はApollo Clientと同様 */

export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {
  const cookies = context.req.headers.cookie; /* session=AAAAAAAAAA... */

  // トークンがない場合ログイン画面にリダイレクト
  if (!cookies) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  const token = cookies.replace('session=', ''); // JWTのみを取り出す

  const res = await api.get('/posts', {
    headers: {
      "Authorization": `Bearer ${token}`
    }
  })
  const postData = res.data;

  return {
    props: {
      postData,
    }
  };
};
```

```api.ts
/* Axios初期化 */

const api = Axios.create({
  baseURL: 'https://api.service.com/',
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
  },
});
```

###ポイント
ApolloClientと同様の流れでcontext → cookie → session(JWT) を取り出します。

今回はAPI呼び出し時にheadersを定義しているのでわかりやすいかと思います。

##あとがき
自分が詰まった箇所だったのでメモがてらまとめてみました。
参考になりましたら幸いです。