---
# System prepended metadata

title: prisma day1
tags: [2024鐵人賽]

---

# prisma day1

## 介紹
這次的鐵人賽想跟大家分享一套自己在平常開發中很常用到的一套 `ORM` 就是 `prisma`，會想用他的原因是因為，去年鐵人賽在介紹 `TRPC` 的時候有用到 `prisma` ，但一直沒有更深入的去理解它，那今年我們就來完成筆者的心願吧～接下來的 30 天會再好好的跟大家補齊一些你不知道的 `prisma` 功能（Ｘ，就請大家好好期待一下～ 

## 優點

大致上先簡單列出，目前筆者覺得 `prisma` 的優勢，之後也會一一跟大家介紹以下的內容～

* 生態系強大
* 良好的 DX 體驗。
* 完整的 `auto generate type` 
* 強大且豐富的 `plugin`

雖然目前市面上很多 `orm` 也符合下面的優勢，例如 `drizzle` 他也是一套有強大的 `type safe` 的 `orm` 甚至 `DX` 也不輸給 `prisma` 筆者其實也很喜歡 XD ，但還是想先整理一下 `prisma` 筆記哈哈，如果讀者有興趣可以來對我指教～

## 開始之前先認識 ORM 吧

在學習 `ORM` 之前筆者先簡單介紹一下使用 `ORM` 的優勢與好處～如圖所見， `ORM` 就是一層 `DB` 跟 `Client` 的中介層，注意一下這邊的 `Client` 並不是 `front end` 中 `user` 使用的 `client` ，這邊得 `client` 指的是 `application` 的地方，也就是你在哪邊使用 `prisma orm` 的語法，因為 `primsa` 可以使用的地方，除了 `backend application` 甚至還可以用在 `serverless applications` 和 `microservices` 等等，所以這邊才會泛指 `client` ~ 那因為我們的 `client` 有非常多種的內容，所以可能會遇到的點是，在不同的 `client` 端點中，彼此需要使用到的 `DB` 功能可能不盡相同，所以對開法者來說，如果有一套 `tool` 可以涵蓋所有的內容，同時簡化 `DB` 語法的話，對於整體的開法效率也會大大提升～於是 `ORM` 得概念就出現了，但同時 `ORM` 就會遇到一個問題是，因為他要對 `DB` 的操作去做映射，所以對 `DB` 功能的支援度就相當重要，所以這也是在技術選型時，要考量的其中一個因素。

![VS--YouTube-WhatIsPrisma-0’05”](https://hackmd.io/_uploads/SJj8KawnA.jpg)
[圖片來源](https://www.prisma.io/orm)

這邊想補充一點就是 `primsa` 支援使用原生 `SQL` 的指令，例如以下的 `queryRaw` 或是 `executeRaw` 等等，所以如果你發現有些 `DB` 中能沒有支援在 `prisma` 使用時，也可以當做備用方案～

### $queryRaw

```typescript
const email = 'emelie@prisma.io'
const result = await prisma.$queryRaw`SELECT * FROM User WHERE email = ${email}`
```

### $executeRaw

```typescript
const result: number =
  await prisma.$executeRaw`UPDATE User SET active = true WHERE emailValidated = true`
```

## schema

這邊要介紹 `ORM` 中最重要的一個部分就是定 `schema` ， `primsa` 很厲害的地方就是它同時支援 `SQL` 以及 `NoSQL` ，甚至對於不同 `SQL` 中有不同的 `Schema` 訂定的方式～

### Relational databases

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

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

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

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

### MongoDB
```typescript
datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

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

model Post {
  id        String  @id @default(auto()) @map("_id") @db.ObjectId
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  String  @db.ObjectId
}

model User {
  id    String  @id @default(auto()) @map("_id") @db.ObjectId
  email String  @unique
  name  String?
  posts Post[]
}
```
上面的 `schema` 可以簡單分成三個部分介紹。

* Data source: 你的 `DB connection` 以及你的 `DB type`。
* Generator: 你希望 `prisma` 怎麼去生成你的 `prisma client`。
* Data model: 定義你的 `db` 的 `models`。

### DB pull

`prisma` 除了可以訂新的 `models` 外，也可以同步已存在的 `models` 到你的 `prisma schema` 中，只要打以下的 `cli`。

```typescript
> npx prisma db pull
```
然後你就會發現你的 `schema` 就多了一些原本 `db` 的 `models` 了～


```typescript
model Post {
  id        String  @id @default(auto()) @map("_id") @db.ObjectId
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  String  @db.ObjectId
}
```

在 `prisma` 中如果你需要表達關聯式，可以透過 `@relation` 、`[]` 來表達是一對一、一對多，或是多對多的關係。

```typescript
model Post {
  id        String  @id @default(auto()) @map("_id") @db.ObjectId
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  String  @db.ObjectId
}

model User {
  id    String  @id @default(auto()) @map("_id") @db.ObjectId
  email String  @unique
  name  String?
  posts Post[]
}
```

## auto type safe

`prisma` 因為是一套有 `type safe` 的 `ORM` ，所以每次做完 `DB pull` 或是 `DB migration` 後，`prisma` 都會在你 `local` 的 `node_modules` 中自動產生所有你需要的 `type`。

![截圖 2024-09-07 中午12.24.43](https://hackmd.io/_uploads/HJm0HUF20.png)


## migrate

如果 `DB` 需要添加新的欄位可以直接在 `schema` 中加上。

```typescript
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
  phone String? // add new filed
}
```
之後跑 `migration cli` 就會跑出下列的訊息，然後會要求你描述這次 `migration` 的 `name` 有點像是 `git` 在提交 `commit` 一樣。

```typescript
> npx prisma migrate dev
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "dev.db" at "file:./dev.db"

? Enter a name for the new migration: › add phone fields in User modal
```

然後 `prisma` 就會跟你你提交的 `name` 自動在你的 `migration` 加新的 
`folder`  

```typescript
✔ Enter a name for the new migration: … add phone fields in User modal
Applying migration `20240907043004_add_phone_fields_in_user_modal`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20240907043004_add_phone_fields_in_user_modal/
    └─ migration.sql

Your database is now in sync with your schema.

✔ Generated Prisma Client (v5.19.1) to ./node_modules/@prisma/client in 51ms
```
然後仔細觀察 `mugrations folder`  就會記錄每次 `migration` 的紀錄，主要是產生原生 `SQL` 指令。

![截圖 2024-09-07 中午12.31.31](https://hackmd.io/_uploads/BJeBvIYnR.png)


## prisma studio

`prisma` 很貼心的提供 `studio` 去查看專案中用到的所有 `model` ，首先先打下方的 `cli`。

```typescript
> npx prisma studio      
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Prisma Studio is up on http://localhost:5555
```

然後打開 `http://localhost:5555` ，你會看到這邊記錄著可以用到的 `model`。

![截圖 2024-09-07 中午12.37.30](https://hackmd.io/_uploads/H1_2_LYnR.png)

然後點進去各自的 `model` 會看到會用到的欄位有哪些，以及目前有哪些資料，那因為還沒有加資料，所以內容會是空的。

![截圖 2024-09-07 中午12.38.40](https://hackmd.io/_uploads/ry7kYIK3R.png)

然後你也可以直接在 `studio` 中直接加資料。

![截圖 2024-09-07 中午12.40.31](https://hackmd.io/_uploads/r1OLFLK3A.png)

加完之後我們可以簡單測試拿資料。

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

const prismaClient = new PrismaClient()
const main = async () => {
  const userLists = await prismaClient.user.findMany()
  console.log(userLists)
}
main()
```

你會發現你在 `studio` 加的 `data`，也成功不再在你的 `client` 了。

```typescript
[
  {
    id: 1,
    email: "some@gmail.com",
    name: "Danny",
    phone: null,
  }
]
```

今天大致上先初探 `prisma` 的內容，明天我們繼續介紹，為何你需要用 `prisma` ，感謝各位讀者的觀看我們明天見～