Prisma + Next.jsでシンプルなCRUD APIを作成する

TL;DR

O/Rマッパとしてprismaを使用して、簡単なCRUDをNext.jsと一緒に作成します。

Next.jsのセットアップ

なにはともあれNext.jsをセットアップします。

yarn create next-app --typescript

prismaのセットアップ

prismaをインストール

# install
yarn add @prisma/client
yarn add prisma --dev

# init
npx prisma init

.envが作成され、その中にDATABASE_URLが設定されています。今回はsqlite3を使います。

.env

DATABASE_URL="file:./dev.db"

schemaを書きます。とりあえず、titleとcontentがあれば良さそうです。

prisma/schema.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Todo {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title String @db.VarChar(255)
  content String?
}

準備ができたので、データベースを起動した後、migrationをし、prisma clientのセットアップをします。

npx prisma migrate dev --name init

migrateをすると自動的に@prisma/clientnode_modules配下に作成されます。明示的にnpx prisma generateをすることでも生成できます。

テーブルができているか一応チェックします。

sqlite3 prisma/dev.db
> .tables
Todo                _prisma_migrations
> .schema TODO
CREATE TABLE IF NOT EXISTS "Todo" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" DATETIME NOT NULL,
    "title" TEXT NOT NULL,
    "content" TEXT
);
>Postgresの場合

herokuかなんかにデプロイする気がするので、postgresを使用する場合についても書いておきます。まず、postgresをdockerで用意しておきます。頻繁にpostgresのdockerを使っているので、使用するportは15432です。

version: "3"
services:
  db:
    image: postgres:13.3
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - 15432:5432
    volumes:
      - ./postgres:/var/lib/postgresql
volumes:
  postgres:

.envのDATABASE_URLを以下のように設定します。portを今回はいじっているのと、パスワード、ユーザーネームが設定されているので注意が必要です。

.env

DATABASE_URL="postgresql://postgres:postgres@localhost:15432/main?schema=public"

prisma clientの設定

libs以下にprisma clientの設定を作成しておきます。どんなクエリが投げられているか一応みたいなので、logにqueryを追加しています。

import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient({
  log: ["query", "error", "info", "warn"],
});
export default prisma;

export * from "@prisma/client";

CRUDできるAPIの作成

今回は単純な確認なので、メソッドの確認は行っていません。また、実際にはパスパラメタなどでIDを受け取ったほうがいいと思いますが、Next.jsでやるのは少し面倒だったので、全てデータの中で受け取っています。

Create

まず、Todoを作成するcreate-todoを作成します。

pages/api/create-todo.ts

import type { NextApiHandler } from "next"
import prisma from "../../libs/prisma"

const handler: NextApiHandler = async (req, res) => {
    try {
        await prisma.todo.create({data: {...req.body, updatedAt: new Date()}})
        res.status(200).send("ok");
    } catch (error) {
        console.log(error)
        res.status(500).json(error)
    }
}

export default handler;

yarn devで起動して、APIにPOSTを投げてみます。

curl -X POST -H "Content-Type: application/json" -d '{ "title": "test", "content": "Test Todo Items" }' localhost:3000/api/create-todo
# ok

READ

TODO LISTを取得するEND POINTを作成します。

pages/api/get-todos.ts

import type { NextApiHandler } from "next"
import prisma from "../../libs/prisma"

const handler: NextApiHandler = async (req, res) => {
    try {
        const todos = await prisma.todo.findMany();
        res.status(200).json(todos);
    } catch (error) {
        console.log(error)
        res.status(500).json(error)
    }
}

export default handler;

GETリクエストしてみます。ちゃんと作成されていることが分かります。

curl localhost:3000/api/get-todos | jq
[
  {
    "id": 1,
    "createdAt": "2021-11-09T00:01:37.223Z",
    "updatedAt": "2021-11-09T00:01:37.215Z",
    "title": "test",
    "content": "Test Todo Items"
  }
]

UPDATE

UPDATE用のAPIを作ります。

pages/api/update-todo.ts

import type { NextApiHandler } from "next"
import prisma from "../../libs/prisma"

const handler: NextApiHandler = async (req, res) => {
    if (!req.body.id) {
        res.status(400).send("Bad Request. need id!");
        return
    }
    try {
        await prisma.todo.update({data: {...req.body, updatedAt: new Date()}, where: {id: req.body.id}})
        res.status(200).send("ok");
    } catch (error) {
        console.log(error)
        res.status(500).json(error)
    }
}

export default handler;

curlでチェックします。

curl -X PUT -H "Content-Type: application/json" -d '{ "id": 1, "title": "test", "content": "Update Test Todo Items" }' localhost:3000/api/update-todo
# ok
curl localhost:3000/api/get-todos | jq
[
  {
    "id": 1,
    "createdAt": "2021-11-09T00:01:37.223Z",
    "updatedAt": "2021-11-09T00:12:38.526Z",
    "title": "test",
    "content": "Update Test Todo Items"
  }
]

contentとupdatedAtがたしかに更新されています。

Delete

最後にDelete部分を実装します。

pages/api/delete-todo.ts

import type { NextApiHandler } from "next"
import prisma from "../../libs/prisma"

const handler: NextApiHandler = async (req, res) => {
    try {
        await prisma.todo.delete({where: {id: req.body.id}})
        res.status(200).send("ok");
    } catch (error) {
        console.log(error)
        res.status(500).json(error)
    }
}

export default handler;

DELETできるか確認します。

curl -X DELETE -H "Content-Type: application/json" -d '{ "id": 1 }' localhost:3000/api/delete-todo
# ok
curl localhost:3000/api/get-todos | jq
# []

この記事に関するIssueをGithubで作成する

Read Next