Closed9

Prismaではじめてのばっくえんど

ONOYAMA ShodaiONOYAMA Shodai

今までフロントばっっかやってきたので、バックもやりたいと思います。
TypeScriptとReact(Next)をやってきたので、同じTypeScriptで書けるPrismaのチュートリアルを触っていきます。
https://www.prisma.io/

ONOYAMA ShodaiONOYAMA Shodai

Make sure you have your database connection URL at hand.

とあるように、PostgreSQLは別途用意していおいて、URLから叩ける状態にする必要があります。今回はDockerの練習も兼ねて、Dockerでザクっと建てる方向で行きます。

まずはPostgreSQL公式のDockerイメージを落とします。これで最新版のPostgreSQLが準備できるはず。

$ docker pull postgres

んで、コンテナを起動。

$ docker run --name posgre-container -d -e POSTGRES_PASSWORD=pass1 postgres

docker psを叩くと…
ウオー!! ちゃんとコンテナ動いてますね。PostgreSQLってデフォルトのポート番号5432なんだ。

$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS      NAMES
cc7a3a0dc888   postgres   "docker-entrypoint.s…"   3 minutes ago   Up 2 minutes   5432/tcp   posgre-container

Docker詳しくないんで調べたら、

  • name: コンテナ名を指名する。
  • -d: デタッチモードにする。
    • コンテナ実行時に使うルートプロセスが終了すると、 --rm オプションを指定していない限りコンテナを終了するんだって。
  • -e: コンテナ内の任意の環境変数を設定する。
    • ここではパスワードが環境変数になっているんかな。
  • 最後のpostgres: さっき落としてきたイメージの名前

みたいな感じらしい。オプション多すぎて毎回設定するの大変そうこなみ

参考

ONOYAMA ShodaiONOYAMA Shodai

PostgreSQL in Dockerはとりあえず動いている前提で進めます。次はPrismaの設定やっていきます。
とりあえずnpm関係のコマンドを叩きましょう。

$ npm init -y 
$ npm install prisma typescript ts-node @types/node --save-dev

tsconfig.jsonを設定します。

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}

んで、npx prismaでPrisma CLIを呼び出せるようにします。

$ npx prisma

◭ Prisma is a modern DB toolkit to query, migrate and model your database (https://prisma.io)

Usage

  $ prisma [command]

Commands

            init   Setup Prisma for your app
        generate   Generate artifacts (e.g. Prisma Client)
              db   Manage your database schema and lifecycle
         migrate   Migrate your database
          studio   Browse your data with Prisma Studio
          format   Format your schema
...

すぐに終わったからinitできてない感あるけど、できてるっぽい。
そして、以下を叩く。

$ npx prisma init

✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.

Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver (Preview) or mongodb (Preview).
3. Run prisma db pull to turn your database schema into a Prisma schema.
4. Run prisma generate to generate the Prisma Client. You can then start querying your database.

More information in our documentation:
https://pris.ly/d/getting-started

このコマンドにより、プロジェクトのルートに、schema.prismaというファイルと.envファイルを含むprismaという新しいディレクトリが作成されます。

schema.prismaには、データベース接続とPrismaクライアント・ジェネレータを含むPrismaスキーマが含まれるらしいです。.envは,環境変数を定義するためのdotenvファイルで、これ以降のデータベース接続に使っていきます。

参考

ONOYAMA ShodaiONOYAMA Shodai

オプション多すぎて毎回設定するの大変そうこなみ

こういう時のためのDocker Composeだろォ!

というわけで、さっき叩いたコマンドをdocker-compose.ymlに書いていきます。

なお環境の再現性を高めるために、イメージのバージョンは3.9に指定することにします。

# ファイルフォーマットのバージョン。
# ファイル作成時点で3が最新かつ安定。
version: "3.9"

# サービスという単位でコンテナを管理。
services:
  postgres-container:
    # DockerリポジトリからPullするDockerイメージ。
    # ファイル作成時点で13.4が最新かつ安定。
    image: postgres:13.4
    # ポート番号を指定。
    # localhostの5432番ポート宛ての通信を、コンテナの5432番ポートに転送。
    ports:
      - 5432:5432
    # サービス(コンテナ)内の環境変数を設定。
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      # 本来、PostgreSQLのデータは/var/lib/postgresql/data直下に作成されるが、
      # Dockerdaで/var/lib/postgresql/dataにデータを作成しようとすると失敗する。
      # そのため、アドホックではあるがデータ作成ディレクトリを1層深くしている。
      PGDATA: /var/lib/postgresql/data/pgdata
    # ホストOS(PC)上にdbボリュームを定義して、
    # PostgresSQLのコンテナの/var/lib/postgresql/data/pgdataというディレクトリを、dbボリュームにマウント。
    volumes:
      - ./db:/var/lib/postgresql/data


そして以下をぶっ叩く。-dを指定すると、コンテナをデーモンとしてバックグラウンドで起動できます。

docker-compose up -d

すると同じようにコンテナが起動します。いいね。

Creating network "prisma-tutorial_default" with the default driver
Pulling postgres-container (postgres:13.4)...
13.4: Pulling from library/postgres
a330b6cecb98: Already exists
3b0b899b4747: Pull complete
cc0b2671a552: Pull complete

...

Digest: sha256:97e5e91582e89514277912d4b7c95bceabdede3482e32395bcb40099abd9c506
Status: Downloaded newer image for postgres:13.4
Creating prisma-tutorial_postgres-container_1 ... done

参考

ONOYAMA ShodaiONOYAMA Shodai

Prisma Migrateをやっていきます。

schema.prismaに、以下の項目を追記します。詳しい事はあとで説明があると思うので、今はコピペ~。

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

model Profile {
  id     Int     @id @default(autoincrement())
  bio    String?
  user   User    @relation(fields: [userId], references: [id])
  userId Int     @unique
}

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

そして以下のコマンドを叩きます。これにより、新しいSQLファイルが作成されて、データベースに対してマイグレーションファイルが実行されます。(実行って何するんですかね…ちょっとまだよく分からん…)

npx prisma migrate dev --name init


できたっぽいですね!
これにより、以下のSQLファイルが生成されます。 何書いてるのか全然分かんねぇぜ!!!

-- CreateTable
CREATE TABLE "Post" (
    "id" SERIAL NOT NULL,
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" TIMESTAMP(3) NOT NULL,
    "title" VARCHAR(255) NOT NULL,
    "content" TEXT,
    "published" BOOLEAN NOT NULL DEFAULT false,
    "authorId" INTEGER NOT NULL,

    CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Profile" (
    "id" SERIAL NOT NULL,
    "bio" TEXT,
    "userId" INTEGER NOT NULL,

    CONSTRAINT "Profile_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "User" (
    "id" SERIAL NOT NULL,
    "email" TEXT NOT NULL,
    "name" TEXT,

    CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Profile_userId_key" ON "Profile"("userId");

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- AddForeignKey
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Profile" ADD CONSTRAINT "Profile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

ちょっと気になったので、マイグレーションという単語を調べてみます。

こちらのサイトによると、マイグレーションとは

DBに保存されているデータを保持したまま、テーブルの作成やカラムの変更などを行うための機能

のことらしいです。データベースの構造を変えるというクリティカルな操作を安全に行うために、RailsやPrismaが使われているってことかぁ。

参考

ONOYAMA ShodaiONOYAMA Shodai

え、プロジェクトのルートのdb/pgdata内にデータマウントされてなくない?生成されたファイル入ってないんだけど?って思ってたら、どうやらマウントディレクトリは特別なヤツらしい。
なんでも仮想マシンで、通常は入れないんだとか。ここではそういうもんだと割り切って進めるかぁ。

ONOYAMA ShodaiONOYAMA Shodai

Prisma Clientをやっていきます。まずはnpm installから。

npm install @prisma/client

Prisma Cliantによって、TypeScriptでデータベースを編集できるようになります。以下にそのスクリプトを書きます。

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function main() {
  // データベースにクエリを送信する
}

main()
  .catch((e) => {
    throw e;
  })
  .finally(async () => {
    // スクリプトが終了したら、データベース接続を閉じる
    await prisma.$disconnect();
  });

このmain()関数内にクエリを書いて、「データベースからすべてのUserレコードを読み込み、結果を表示する」操作を行います。

async function main() {
  // データベースにクエリを送信する
  const allUsers = await prisma.user.findMany();
  console.log(allUsers);
}

そしてnpxで実行すると…

npx ts-node index.ts


いいね。今はデータベースに何も入っていないから、空の配列が返されていますね。

同様に、今度はデータベースに書き込みをしていきます。

async function main() {
  await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'alice@prisma.io',
      posts: {
        create: { title: 'Hello World' },
      },
      profile: {
        create: { bio: 'I like turtles' },
      },
    },
  })

  const allUsers = await prisma.user.findMany({
    include: {
      posts: true,
      profile: true,
    },
  })
  console.dir(allUsers, { depth: null })
}

そして同様にnpxで実行すると...

いいね。userにユーザー情報を追加して表示できていますね。
データの更新をするには

async function main() {
  const post = await prisma.post.update({
    where: { id: 1 },
    data: { published: true },
  })
  console.log(post)
}

を追加すればいいですね。実行すると

のように、POSTのデータが取得できますね

参考

ONOYAMA ShodaiONOYAMA Shodai

PostgreSQLのコンテナを止めると、こんな感じで404みたいなやつを返してくれました。

このスクラップは2021/09/21にクローズされました