😸

3. Prisma Clientを使ってREST APIの作成 Prisma 入門ワークショップ(2021)

2021/06/25に公開

ゴール

このレッスンの目標は、Prisma Clientについて学んだばかりの知識を使って、Expressを使ってREST APIのいくつかのルートを実装することです。

レッスン

0. Prisma 入門ワークショップ(2021)
1. Prismaのセットアップ
2. Prisma Clientを使ってみる
3. Prisma Clientを使ってREST APIの作成←ここ
4. Prisma Clientを使ってGraphQL APIの作成

リソース

Prismaとは?
Node.js & TypeScript向けの完璧なORM

英語のドキュメント
https://www.notion.so/A-Practical-Introduction-to-Prisma-2021-ccf00a066ef4432caeb03da179e38302

Zoom ウェビナー
https://www.youtube.com/watch?v=80CYmde9z9c

GitHub
https://github.com/nikolasburk/prisma-workshop

セットアップ

レッスン1で設定したのと同じ prisma-workshop プロジェクトで作業を続けることができます。ただし、このレッスンのスターターは、クローンしたレポの rest-api ブランチにあります。

そのブランチに切り替える前に、プロジェクトの現在の状態をコミットする必要があります。簡単にするために、stashコマンドを使ってこれを行うことができます。

git stash

このコマンドを実行したら、rest-api ブランチに切り替えて、現在の migrations ディレクトリと dev.db ファイルを削除することができます。

git checkout rest-api
rm -rf prisma/migrations
rm prisma/dev.db

次に、npmの依存関係を一掃し、package.jsonにある新しい依存関係を考慮して再インストールします。

rm -rf node_modules
npm install

ここで使用するデータモデルは、先ほど作成したものと非常によく似ています。ただ、Postモデルにいくつかのフィールドを追加して拡張しています。

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String
  content   String?
  published Boolean  @default(false)
  viewCount Int      @default(0)
  author    User?    @relation(fields: [authorId], references: [id])
  authorId  Int?
}

Prismaのセットアップを最初からやり直すので、データベースとそのテーブルを再作成する必要があります。以下のコマンドを実行して、これを行います。

npx prisma migrate dev --name init

最後に、prisma/seed.tsファイルで指定されたサンプルデータをデータベースにシードします。このseedスクリプトは、以下のコマンドで実行できます。

npx prisma db seed --preview-feature

以上で、タスクの準備が整いました。

タスク

このレッスンのタスクは、TODOと書かれたsrc/index.tsファイルの中にあります。目標は、各REST APIルートに正しいPrismaクライアントのクエリを挿入することです。

これはAPI設計のレッスンではありませんので、実際のアプリケーションでのAPI操作の設計については、より慎重に考える必要があることに注意してください。

VS Codeを使用している場合は、REST Clientエクステンションをインストールし、test.httpで提供されているHTTPコールを使用して実装をテストすることができます。

VS Code REST Clientエクステンションのクイックデモを見る

GET /users

すべてのユーザーを取得します。

答え
app.get("/users", async (req, res) => {
  const result = await prisma.user.findMany()
  res.json(result)
});

POST /signup

新しいユーザーを作成します。

答え
app.post(`/signup`, async (req, res) => {
  const { name, email } = req.body;

  const result = await prisma.user.create({
    data: {
      name,
      email
    }
  })

  res.json(result)
});

POST /post

新しいPostを作成します。

答え
app.post(`/post`, async (req, res) => {
  const { title, content, authorEmail } = req.body;

  const result = await prisma.post.create({
    data: {
      title,
      content,
      author: {
        connect: {
          email: authorEmail
        }
      }
    }
  })

  res.json(result)
});

PUT /post/:id/views

Postのビュー数を1増やします。

答え
app.put("/post/:id/views", async (req, res) => {
  const { id } = req.params;

  const result = await prisma.post.update({
    where: {
      id: Number(id),
    },
    data: {
      viewCount: {
        increment: 1,
      },
    },
  });

  res.json(result);
});

PUT /publish/:id

Postを公開します。

答え
app.put("/publish/:id", async (req, res) => {
  const { id } = req.params;

  const result = await prisma.post.update({
    where: { id: Number(id) },
    data: {
      published: true,
    },
  });

  res.json(result);
});

GET /user/:id/drafts

特定のユーザーの未公開のPostを取得します。

答え
app.get("/user/:id/drafts", async (req, res) => {
  const { id } = req.params;

  const result = await prisma.user.findUnique({
    where: { id: Number(id) },
  }).posts({
    where: {
      published: false
    }
  })

  res.json(result)
});

GET /post/:id

指定したIDと一致するPostを取得します。

答え
app.get(`/post/:id`, async (req, res) => {
  const { id } = req.params;

  const result = await prisma.post.findUnique({
    where: { id: Number(id) },
  });

  res.json(result);
});

GET /feed?searchString=<searchString>&skip=<skip>&take=<take>

公開されているすべてのPostを取得し、検索文字列がタイトルやコンテンツに含まれているかどうかをチェックして、ページネーションやフィルタリングを行います。

答え
app.get("/feed", async (req, res) => {
  const { searchString, skip, take } = req.query;

  const or = searchString ? {
    OR: [
      { title: { contains: searchString as string } },
      { content: { contains: searchString as string } },
    ],
  } : {}

  const result = await prisma.post.findMany({
    where: {
      published: true,
      ...or
    },
    skip: Number(skip) || undefined,
    take: Number(take) || undefined,
  });

  res.json(result);
});

Discussion