Oops!!

プログラミングとかのラフなブログ

Next.js(App Router) + NextAuth.js + GraphQL Nexus + ApolloでBoilerplateを作ってみた

Next.js(App Router) + NextAuth.js + GraphQL Nexus + ApolloでBoilerplateを作ってみた

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を利用しても新規作成やパスワードの設定はできません。

その為、こちらは自前で実装します。

  1. メールアドレスとパスワードをServerにPOSTします。
  2. Serverではパスワードを暗号化して保存します。
  3. 保存完了後、NextAuth.jsのsignInを利用します。
  4. credentialsのauthorizeで暗号化されたパスワードと整合が取れるかチェックします。
  5. 問題なけれがユーザー情報を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対応については実験的なものが多いので、少し様子を見た方が良いかもしれません。

余談ですが、毎回技術系の記事を書いてると作ってる時に何に悩んでたのかとか思い出せなくて執筆が進まなくなってしまいます。

プロフィール画像

すずき ゆうた

愛知県でフリーランスのフロントエンド・エンジニアをしています。Reactを用いた開発が得意です。 他にもプロジェクトマネジメントや組織マネジメントも行ってきました。エビデンスのない事でも自分の経験から書いていくので話半分くらいでお願いします。