💬

Prisma+Next.js+Route HandlerでAPIを実装する

に公開

この記事について

Prismaで1:NとかM:Nとかのリレーションをどう実装するか。また、それらのAPI実装をNext.jsのRoute Handlerでどうするか、について書きます。

テーブル実装

記事にコメントとカテゴリがつく、みたいなやつです。記事とコメントは1:N、記事とカテゴリはM:Nという感じです

ER図

PrismaにおけるM:Nについて

Prismaでは、M:Nの実装について、明示的定義と暗黙的定義の2種類が存在します。
明示的定義は、中間テーブルをschema.prismaに実装する、暗黙的定義の場合は実装しない、というのが違いです。前者の場合、中間テーブルの実装が必要ですが、中間テーブルに関連性以外の情報を持たせることができます。後者の場合、実装は不要ですが、関連性以外の情報は持たせられません。ただ、どちらの場合も、中間テーブルはちゃんと作られます。
詳細:https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/many-to-many-relations
なお、今回のサンプル実装は暗黙的定義です

API実装

サンプル実装です。エラー処理とかバリデーションチェックは入れてません。あと、ややAIに任せた部分もあるので、そこは多めに見てください
https://github.com/kassytt/apiapp

Category API

これについては、特にいうことはないかなと。Articleとの紐付けは、Aricles APIの方で解説します
https://github.com/kassytt/apiapp/blob/main/src/app/api/categories/route.ts

https://github.com/kassytt/apiapp/blob/main/src/app/api/categories/[id]/route.ts

Comment API

Article:Commentで1:Nの関係になっていますので、POSTなどで、ArticleIDが必要になりますが、それ以外はCategory APIとあまりかわらないかなと
https://github.com/kassytt/apiapp/blob/main/src/app/api/comments/route.ts

https://github.com/kassytt/apiapp/blob/main/src/app/api/comments/[id]/route.ts

Articles API

CategoryとはM:N、Commentとは1:Nの関係です
https://github.com/kassytt/apiapp/blob/main/src/app/api/articles/route.ts

https://github.com/kassytt/apiapp/blob/main/src/app/api/articles/[id]/route.ts

GETで紐づくデータを全部取りたい時はincludeを使う

Articleに紐づくCategoryとCommentも取りたい場合はincludeを使いましょう。includeがないと

  const result = await prisma.article.findMany({
    include: {
      categories: true,
      comments: true
    }
  });

こんな感じで取得できます

curl -s GET http://localhost:3000/api/articles/2 | jq .
{
  "id": 2,
  "title": "新しいタイトル",
  "content": "更新された本文です",
  "createdAt": "2025-10-23T06:10:14.443Z",
  "updatedAt": "2025-10-23T06:10:14.443Z",
  "categories": [
    {
      "id": 1,
      "category": "日常生活",
      "createdAt": "2025-10-23T06:09:49.937Z",
      "updatedAt": "2025-10-23T06:09:49.937Z"
    },
    {
      "id": 2,
      "category": "ソフトウェアエンジニアリング",
      "createdAt": "2025-10-23T06:10:07.567Z",
      "updatedAt": "2025-10-23T06:10:07.567Z"
    }
  ],
  "comments": [
    {
      "id": 1,
      "comment": "はじめてのNext.js記事へのコメント",
      "articleId": 2,
      "createdAt": "2025-10-23T06:11:18.123Z",
      "updatedAt": "2025-10-23T06:11:18.123Z"
    },
    {
      "id": 2,
      "comment": "はじめてのNext.js記事へのコメントのコメント",
      "articleId": 2,
      "createdAt": "2025-10-29T02:31:46.196Z",
      "updatedAt": "2025-10-29T02:31:46.196Z"
    }
  ]
}

関連テーブル更新時のconnect / set / disconnectの使い分け

例えば、Article登録時にCategoryも登録したい場合は、connectまたはsetを使います。この際、リクエストにどのCategoryを登録するかをidで指定します。

const result = await prisma.article.create({
    data: {
      title,
      content,
      categories: {
        // リクエストで指定されたcategoryのidを紐づけている
        connect: categoryIds.map((id: number) => ({ id })),
      },
    },
  });

他にもsetとdiscconnectという似たような性質のものがありますが、以下のように使い分ければ良いかと思います。あくまで目安なので要件次第ではあります

処理の目的 Prismaの操作 よく使うHTTPメソッド
新しい記事を作成し、既存カテゴリを紐付ける connect POST
記事にカテゴリを追加する(部分更新) connect PATCH
記事のカテゴリを完全に入れ替える set PUT
記事からカテゴリを外す disconnect PATCH / PUT

まとめ

Prisma + NextJS + Route HandlerでAPIを実装する記事があまりなかった気がしたので書いてみました。これを使うと、NextJSでフルスタックっぽく開発ができるのはいいですね。

参考サイト

NextJSとPrismaの公式サイトです。書いた内容はここに詳しく書かれています
https://nextjs.org/docs/app/getting-started/route-handlers

https://www.prisma.io/docs/orm/prisma-schema/data-model/relations#relations-in-the-database

Discussion