##概要
Next.jsではCSR, SSR, SSG, ISRといった色々なレンダリング方法を使い分けることが可能です。
SSGやISRはビルド時にhtmlが生成されるためパフォーマンスが最も高く、Next.jsの開発元であるVercelも推奨しています。
ただビルド時だとユーザーのリクエストを受け取れないため、例えばログイン状態でAPIを叩いてレンダリングさせたい場合などはSSGでなくSSRを使ってPre-renderingするのがいいかと思います。
今回はSSRで実際どのように認証情報(JWT)を乗せてリクエストを送るのか、Apollo Clientとaxiosの2パターンを紹介しようと思います
参考
SSG と SSR で理解する Next.js のページレンダリング
##前提
JWTをサーバー側に渡すにはcookieを使います
今回はnookiesというライブラリを使います
ログイン処理にて session
という名前でトークンを保存しておきます
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
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
},
};
}
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
/* クライアント側は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,
}
};
};
/* 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を定義しているのでわかりやすいかと思います。
##あとがき
自分が詰まった箇所だったのでメモがてらまとめてみました。
参考になりましたら幸いです。