Next.jsのApp Routerが正式にリリースされたので、自分が好きなGraphQL Nexus、使ったことのほとんどないNextAuth.jsと勉強を兼ねて組み合わせて作ってみました。
作ったものはGithubにあります。
nextjs-app-router-authjs-graphql-nexus | Github
App Routerの説明は公式を見てもらうのが一番早いと思うので、自分がハマったところを抜粋して説明していきます。
Apollo Serverを立てる
App Routerを使用してもpages/api
は今まで通り使用できましたが、export { handler as GET, handler as POST }
でexportすることでapp/api
配下で実装できます。
// src/app/api/graphql/route.ts
import { ApolloServer } from '@apollo/server'
import { startServerAndCreateNextHandler } from '@as-integrations/next'
import { context } from '@/graphql/context'
import { schema } from '@/graphql/schema'
const server = new ApolloServer({ schema })
const handler = startServerAndCreateNextHandler(server, {
context,
})
export { handler as GET, handler as POST }
Apollo Serverはほぼドキュメント通りですが、schemaとcontextを設定しています。@/graphql/context.ts
ではprismaやNextAuth.jsのsessionを呼び出しています。
今回はNexusを使用しているので、そちらでGraphQL Schemaを生成していますが、こちらは好きなものに変更していただいても問題ありません。
providerの作成
src/app/provider.ts
でApolloとNextAuth.jsのproviderを設定しています。
// src/app/provider.ts
'use client'
import { ApolloNextAppProvider } from '@apollo/experimental-nextjs-app-support/ssr'
import { SessionProvider } from 'next-auth/react'
import { makeClient, makeSuspenseCache } from '@/libs/apollo-client'
interface Props extends React.PropsWithChildren {}
export const Provider = ({ children }: Props) => {
return (
<ApolloNextAppProvider
makeClient={makeClient}
makeSuspenseCache={makeSuspenseCache}
>
<SessionProvider>{children}</SessionProvider>
</ApolloNextAppProvider>
)
}
Next.jsのApp Routerに対応したものを、experimental-nextjs-app-support
として試験的にapolloが提供しています。
apollo-client内の記述は公式のブログで紹介されてたものを利用しています。
ユーザーアカウントの作成
今回はメールアドレス、パスワードでログインできるようにしたいと思いました。しかしながらNextAuth.jsのcredentialsを利用しても新規作成やパスワードの設定はできません。
その為、こちらは自前で実装します。
- メールアドレスとパスワードをServerにPOSTします。
- Serverではパスワードを暗号化して保存します。
- 保存完了後、NextAuth.jsのsignInを利用します。
- credentialsのauthorizeで暗号化されたパスワードと整合が取れるかチェックします。
- 問題なけれがユーザー情報をreturnしてsessionに保存されます。
セッションの情報をDBに保存するやり方もありましたが、今回はそこはあまり重要ではなかったので上記のような対応にしています。
GraphQLのresolverでsessionを受け取る
ここではcontext.tsでgetTokenを使ってsessionを受け取っています。
// src/graphql/context.ts
import { PrismaClient } from '@prisma/client'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { User } from 'next-auth'
import { getToken } from 'next-auth/jwt'
import { prisma } from '@/libs/prisma-client'
export interface SessionUser extends Omit<User, 'id'> {
id: number
uid: string
}
export interface Context {
req: NextApiRequest
res: NextApiResponse
prisma: PrismaClient
session: SessionUser
}
export const context = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({ req })
return {
req,
res,
prisma,
session,
}
}
まとめ
ApolloやNextAuth.jsのApp Router対応をしていたのですが、まだ実験的なものも多くドキュメントも少ないためハマりつつ試しながら実装しました。しかしながら出来てしまうと取り立てて複雑なことはないように感じます。
前述の通りまだ各ライブラリもApp Router対応については実験的なものが多いので、少し様子を見た方が良いかもしれません。
余談ですが、毎回技術系の記事を書いてると作ってる時に何に悩んでたのかとか思い出せなくて執筆が進まなくなってしまいます。