🗂

【2022最新版?】Next.js+Prisma+PlanetScale+Firebase AuthでTodoアプリのテンプレート

2022/12/17に公開

こんにちは。気づけば年末です。
今年1年を振り返って、「Webアプリ新規で作ってないなぁ。Nextjsの変更も追えてないしPlanetScaleも触ってみたい」と思ったので、認証API付きTodoアプリを作ってみました。
結構実用的なテンプレートができたのではないかと思っています。

作ったもの

コードはこちら

https://github.com/dl10yr/nextjs-with-api-template

ページ構成

  • /: index
  • /todo/ssr: SSR で DB から Todo を取得して表示してます
  • /todo/csr: CSR で DB から Todo を取得して表示、フォームを使って投稿。

API

Next.js でバックエンドの API も構築しています。firebase のidTokenBearer認証しています。

  • GET /api/todos
  • POST /api/todos
  • DELETE /api/todos/{id}

感想

prisma

超便利。yarn prisma db pushでテーブル変更できるの便利すぎる
テーブル定義はこんな感じ
https://github.com/dl10yr/nextjs-with-api-template/blob/main/prisma/schema.prisma

API Routes

何気に使うのは初めて。簡単なCRUDならこれで十分では??

公式のサンプルを見ると、methodでswitchさせるのがちょっと微妙だったので、
https://github.com/vercel/next.js/blob/canary/examples/api-routes-rest/pages/api/user/[id].ts

この方のコードをとても参考にさせていただきました。
https://zenn.dev/nalo/articles/next-api-routes-error-handling

最終的にはこんな感じ
https://github.com/dl10yr/nextjs-with-api-template/blob/main/lib/server/apiRoute.ts

アクセストークン(firebase auth匿名ログイン)の検証

せっかくなので、認証付きAPIを作ってみました。↑のapiRouteの中に実装して共通化しました。
(本当はNext.jsのmiddlewareを使ってみたかったけど、エラー出てちょっと難しそうだったので今回はこっちで)
https://github.com/dl10yr/nextjs-with-api-template/blob/main/lib/server/firebaseAdmin.ts
https://github.com/dl10yr/nextjs-with-api-template/blob/main/lib/server/apiRoute.ts#L18-L38

  • NextApiRequestの型を拡張して、userを追加

クライアント

Next.js 13のAppDirectoryを使いました

認証周り

Firebase Authの匿名ログインを使っています。
ログイン周りの処理の共通化はcatnoseさんのこちらの記事を参考にしています。
https://zenn.dev/catnose99/articles/2169dae14b58b6

API叩くときは、axiosのinterceptor使って、idToken付けてます
https://github.com/dl10yr/nextjs-with-api-template/blob/main/lib/client/api/index.ts#L8-L19

ライフサイクル周りがちょっとハマった

firebaseログインチェックが終わってからクライアントでGET /todos叩くを行わないと、GET /todosを叩くときにidTokenがうまく取れない。このタイミング調整に若干ハマりました。最終的にはisCheckingでログインチェック中は、ページをrenderしないようにしました。

layout.tsx
'use client'
import { useCurrentUser } from '@/lib/client/hooks/useCurrentUser'

export default function TodosLayout({ children }: { children: React.ReactNode }) {
  const { isChecking } = useCurrentUser()
  if (isChecking) return <div>isLoginChecking...</div>
  return (
    <div className="min-h-screen flex flex-wrap">
      <div className="w-full">{children}</div>
    </div>
  )
}

最近Nuxtを触る機会が多いので、Nuxtだとmiddlewareとか、onMountedとか、名前がついてて、順番がわかりやすいよなとは感じました。

SSR時のrevalidateが怪しい

https://beta.nextjs.org/docs/api-reference/segment-config#revalidate

revalidate = 0, dynamic = 'force-dynamic', fetchCache = 'force-no-store'にして、DBからのgetTodosをキャッシュしないようにしたいが、結構変更が反映されない

console.logで見たらgetTodosが呼び出されないときが結構あるみたい

https://github.com/dl10yr/nextjs-with-api-template/blob/main/app/todo/ssr/page.tsx#L3-L16

Githubのディスカッションにも上がってるが、バグってるのか、使い方が悪いのか...
(AppDirectory自体experimentalですし、ちょっと様子見かな)
https://github.com/vercel/next.js/discussions/42290

気になってる点

  • terraformvercelにデプロイ
    • awsではterraform使ってるのでvercelでも使ってみたい
  • PlanetScaleが結構遅い
    • POST、DELETEのAPIで2秒くらいかかるときがある。遅くね??なんか、まずい設定したかな...

Discussion