# prisma
###### tags: `db`
```prisma
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
//@relation用法
@relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?)
```
ReferentialAction: Cascade(referenceid 資料刪除對應的reference也會delete)| NoAction
[@relation](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#relation)
### uuid type
@db.Uuid 這個decorator只能使用在postgresql 中使用讓id生成是uuid type
```typescript
model User {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
test String?
}
```
```typescript
model User{
id Int @id @default(autoincrement())
name String @map("Name")
firstName String @map("first_name")
Post_Post_authorToUser Post[] @relation("Post_authorToUser")
Post_Post_favoritedByToUser Post[] @relation("_Post_favoritedByToUser")
@@map("users")
}
//SQL result
-- CreateTable
CREATE TABLE "users" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"Name" TEXT NOT NULL,
"first_name" TEXT NOT NULL
);
model Post{
id Int @id @default(autoincrement())
author Int
favoritedBy Int?
User_Post_authorToUser User @relation("Post_authorToUser",fields: [author],references: [id],onDelete: NoAction,onUpdate: NoAction)
User_Post_favoritedByToUser User? @relation("_Post_favoritedByToUser",fields: [favoritedBy],references: [id],onDelete: NoAction,onUpdate: NoAction)
}
// SQL result
CREATE TABLE "Post" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"author" INTEGER NOT NULL,
"favoritedBy" INTEGER,
CONSTRAINT "Post_author_fkey" FOREIGN KEY ("author") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT "Post_favoritedBy_fkey" FOREIGN KEY ("favoritedBy") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
);
```
* `@@map("users")` : 更改table name
* `name String @map("Name")` : 更改 field name
### Enabling full-text search
For POSTGRESQL
```typescript
//schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch"]
}
```
For MySQL
```
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch", "fullTextIndex"]
}
```
```
// All posts that contain the word 'cat'.
const result = await prisma.posts.findMany({
where: {
body: {
search: 'cat',
},
},
})
```
```
// All posts that contain the words 'cat' or 'dog'.
const result = await prisma.posts.findMany({
where: {
body: {
search: 'cat | dog',
},
},
})
// All drafts that contain the words 'cat' and 'dog'.
const result = await prisma.posts.findMany({
where: {
status: 'Draft',
body: {
search: 'cat & dog',
},
},
})
```
## mongodb null type filter
```typescript
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
name String?
}
```
在 prisma 如果你的 field 是 optional 你可以加 null 去塞值這時 `prisma studio` 跟 `mongodb` 結果如下:
```typescript
const createNull = await prisma.user.create({
data: {
email: 'user1@prisma.io',
name: null,
},
})
console.log(createNull)
```
```typescript
// in prisma studio
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
}
// in mongodb
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
}
```
但在 `prisma` 中 optional 的欄位其實你可以不加 `prisma` 會自動幫你補 null
```typescript
const createMissing = await prisma.user.create({
data: {
email: 'user2@prisma.io',
},
})
console.log(createMissing)
```
```typescript
// in prisma studio
{
id: '6242c4ae032bc76da250b208',
email: 'user2@prisma.io',
name: null
}
```
此時你到 mongodb 查詢你會發現 name 欄位並不會幫你自動加 null
```typescript
// in mongodb
{
"_id": "6242c4af032bc76da250b208",
"email": "user2@prisma.io"
}
```
這部分是因為 `prisma` 目前無法關聯 null 到 mongodb 中,此時如果你做
query 查詢你會發現結果並不是你的預期:
```typescript
const findNulls = await prisma.user.findMany({
where: {
name: null,
},
})
console.log(findNulls)
```
這時你只會返回 user1 的資料 user2 則沒有,原因是所有的 query 查詢都會是直接從 db 拿資料,而 `prisma studio` 只是一個查看的 `dashbord` 而已,所以你無法從 studio 確定資料正確性,實際上還是要從 db 查看。
```typescript
[
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
}
]
```
所以這時你需要加一個查詢條件 `isSet: false` 這是 `mongodb` 限定的 `filter`
```typescript
const result = await prisma.user.findFirst({
where: {
OR: [
{
name: null
},
{
name: {
isSet: false
}
}
]
}
})
```
這樣所有欄位都包含到拉~
```typescript
[
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
} ,
{
id: '6242c4ae032bc76da250b208',
email: 'user2@prisma.io',
name: null
}
]
```
## type safe
大家對於 typesafe 得第一直覺應該是 ts ,沒錯 ts 確實幫我們處理 type 的一切事情,但這僅限在開發模式,什麼意思呢?原因是 ts 只是一個型別推論 ( type inference ) 與型別註記 ( type annotation ) ,他並不能保證 runtine time 不會有預期外的 input 輸入,而 prisma 有一個 validate 的機制,概念就是會檢查 ORM 的 input ,確定沒問題才會寫入 db 。
而 `type safe` 本質上就是需要確保 `runtime `沒有意外 `input`,而 `typescript` 顯然是不行如下圖。

這邊我們就使用一套有 `typesafe` 的 `tool` `zod`來幫我們完成這件事。
## schema
```typescript
// prisma.schema
model Product {
id String @id @default(cuid())
slug String
name String
description String
price Int
}
```
## validate
這邊我們可以透過 `zod` 寫 `product` 得 `validate` 判斷,然後透過 `Prisma.defineExtension` 去加入我們 `input validate` 的 `function`,當成 `prisma` 得 `extension` 給之後的 `prisma client` 引用。
```typescript
import { Prisma } from "@prisma/client";
import { z } from "zod";
const productSchema = z.object({
slug: z.string().max(100)
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
name: z.string().max(100),
description: z.string().max(100),
price: z.number()
}) satisfies z.Schema<Prisma.ProductCreateInput> // check object type instead of interface type
export const ProductValidation = Prisma.defineExtension({
query: {
product: {
create: ({ args, query }) => {
args.data = productSchema.parse(args.data)
return query(args)
},
update: ({ args, query }) => {
args.data = productSchema.partial().parse(args.data)
return query(args)
},
updateMany: ({ args, query }) => {
args.data = productSchema.partial().parse(args.data)
return query(args)
},
upsert: ({ args, query }) => {
args.create = productSchema.parse(args.create)
args.update = productSchema.partial().parse(args.create)
return query(args);
}
}
}
})
```
## use extenstions
接著把 `ProductValidation` 加到 `prisma client` 中,如此一來就完成拉~最後我們看一下結果。
```typescript
import { ProductValidation } from "./model/product";
import { ZodError } from "zod";
const prisma = new PrismaClient()
.$extends(ProductValidation)
const main = async () => {
await prisma.product.deleteMany()
// validate product
const product = await prisma.product.create({
data: {
slug: "example-product",
name: "Example Product",
description: "Lorem ipsum dolor sit amet",
price: 100
}
})
try {
const inValidateProduct = await prisma.product.create({
data: {
slug: "example-",
name: "Example Product",
description: "Lorem ipsum dolor sit amet",
price: 100
}
})
} catch (e) {
if (e instanceof ZodError) {
console.log(e.errors[0])
}
}
console.log(product)
}
```
如此一來我們就可以確保寫入 `db`的 `input` 值都是預期的~而 `typeSafe` 不僅僅只是檢查型別而已,甚至可以判斷資料大小或是其他 `regex` 的使用,如`productSchema` 的寫法,所以 `typesafe` 在使用上靈活度是非常高的。
```typescript
{
validation: 'regex',
code: 'invalid_string',
message: 'Invalid',
path: [ 'slug' ]
}
{
id: 'cln1eh4br0000uaex7qpt7jrq',
slug: 'example-product',
name: 'Example Product',
description: 'Lorem ipsum dolor sit amet',
price: 100
}
```