🐙

学習ログ|Prisma × Next.js × Docker ビルドで出たエラーと学び

に公開

はじめに

Docker で Next.js + Prisma アプリをビルドするときに、いくつかハマりどころがあった。ここでは実際に遭遇したエラーと、それを直す過程で学んだことを記録する。


前提知識

Prisma

  • Node.js で使える ORM(Object Relational Mapping)。
  • Prisma Client を自動生成して DB に型安全にアクセスできる。
  • @prisma/clientdependencies に入れ、npx prisma generate を実行すると、node_modules/@prisma/client 以下にクライアントコードが生成される。

next.config.ts

  • Next.js のプロジェクト全体の設定ファイル。
  • ここに実験的機能や外部パッケージの扱いを定義できる。
  • バージョンによってキーの名前が変わることがある(今回のケースでは serverComponentsExternalPackagesserverExternalPackages)。

App Router

  • Next.js 13 以降で導入された新しいルーティングシステム。
  • API ルートは src/app/api/.../route.ts に定義し、GET / POST / DELETE などの関数をエクスポートする。
  • 関数の引数型は Next.js 側に決まっていて、誤るとビルドエラーになる。

ESLint

  • JavaScript/TypeScript の静的解析ツール。
  • コードスタイルや型の安全性を保つためにルールを設定できる。
  • 今回は「any を禁止する」というルールに引っかかってビルドが失敗した。

学んだエラーと対応

1. Prisma Client が見つからない

Error: Cannot find module ‘/app/node_modules/@prisma/client/generator’
  • 原因

    • @prisma/clientdevDependencies に入っていて本番では消えていた
    • npx prisma generate を schema コピー前に実行していた
  • 対応

    • @prisma/clientdependencies に移動
    • Dockerfile で schema をコピーした後に npx prisma generate を一度だけ実行
    • または postinstall に任せて、Dockerfile 側では実行しない

2. next.config.ts の設定警告

Unrecognized key(s) in object: ‘serverComponentsExternalPackages’
  • 原因
    Next.js 15 からキー名が変更された

  • 対応

    // before
    experimental: {
      serverComponentsExternalPackages: ['@prisma/client'],
    }
    
    // after
    serverExternalPackages: ['@prisma/client'],
    
    

3. ESLint による any 禁止

Unexpected any. Specify a different type.
  • 原因: ESLint が any を禁止していた
  • 対応: unknown を使い、最低限の型チェックを追加
type CastInput = { name: string }

export async function POST(req: Request) {
  const data: unknown = await req.json()

  if (typeof data !== 'object' || data === null || typeof (data as any).name !== 'string') {
    return new Response(JSON.stringify({ error: 'Invalid payload' }), { status: 400 })
  }

  const input = data as CastInput
  return new Response(JSON.stringify({ ok: true }))
}
  • 一時的に無効化したい場合は、その行にだけ ESLint コメントを付ける
// eslint-disable-next-line @typescript-eslint/no-explicit-any

4. App Router の型エラー

Route "src/app/api/casts/[id]/route.ts" has an invalid "DELETE" export
  • 原因: 第二引数の型を { params: { id: string } } と固定していたが、Next.js は Record<string, string | string[]> を期待している
  • 対応
export async function DELETE(
  req: Request,
  { params }: { params: Record<string, string | string[]> }
) {
  const raw = params.id
  const id = Array.isArray(raw) ? raw[0] : raw

  if (!id) {
    return new Response(JSON.stringify({ error: 'Missing id' }), { status: 400 })
  }

  return new Response(JSON.stringify({ ok: true }))
}

まとめ

  • Prisma → @prisma/client は dependencies に入れる & 生成タイミングを整理
  • next.config.ts → バージョンアップでキー名が変わることがある
  • ESLint → any 禁止ルールに従い、unknown + 型ガードで安全に処理
  • App Router → 引数型は Next.js が定める形(Record<string, string | string[]>)に合わせる

タグ

#Nextjs #Prisma #Docker #TypeScript #学習ログ

Discussion