TypeORM에 이어 타입스크립트 ORM으로 널리 사용되고 있기도 하고, 데이터베이스 마이그레이션에서도 사용되는 Prisma에 대해 알아보고자 한다. TypeORM과는 또다른 사용 방법을 가지고 있으며, 자체적으로 제공하는 스키마 형성 방식을 이용하면 SQL 쿼리를 직접 짜지 않아도 되는 장점이 있으며, 제공하는 ORM 메서드들 또한 다른 ORM과 비교했을 때 풍부함을 느낄 수 있다.
특히 서버리스 오픈소스 플랫폼인 Supabase에서 공식적으로 데이터베이스를 관리할 때 Prisma를 권장하고 있으며, 뿐만 아니라 여느 RDBMS와 같이 Prisma Studio라는 자체 GUI도 제공하는데, 이는 소프트웨어 설치가 필요하지 않아 더욱더 편리하게 느껴졌다.
Prisma란?
Node.js 및 TypeScript용 오픈 소스 ORM으로, TypeORM과 Sequelize와 유사하게 사용된다.
Prisma의 필요성
SQL 쿼리문을 직접 작성할 필요가 없어진다.
Prisma에 존재하는 메소드들을 이용하여 DB를 쉽게 다룰 수 있다.
ORM으로 사용하는 것뿐만 아니라, migration을 하는데에도 유용하게 사용될 수 있다.
Prisma 세팅
Prisma 설치
npm install prisma --save-dev
데이터베이스 연결 설정
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
환경변수 설정
DATABASE_URL="file:./dev.db"
모델 예시
model User {
id Int @default(autoincrement()) @id
email String @unique
name String?
posts Post[]
}
model Post {
id Int @default(autoincrement()) @id
title String
content String?
published Boolean? @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
Prisma Client
CRUD
Create
- Create a single record: create
const user = await prisma.user.create({
data: {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
},
})
- Create a single record using generated types: create
import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
let includePosts: boolean = false
let user: Prisma.UserCreateInput
// Check if posts should be included in the query
if (includePosts) {
user = {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
posts: {
create: {
title: 'Include this post!',
},
},
}
} else {
user = {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
}
}
// Pass 'user' object into query
const createUser = await prisma.user.create({ data: user })
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})
- Create multiple records : createMany
const createMany = await prisma.user.createMany({
data: [
{ name: 'Bob', email: 'bob@prisma.io' },
{ name: 'Bobo', email: 'bob@prisma.io' }, // Duplicate unique key!
{ name: 'Yewande', email: 'yewande@prisma.io' },
{ name: 'Angelique', email: 'angelique@prisma.io' },
],
skipDuplicates: true, // Skip 'Bobo'
})
→ Q: 유니크 키가 자동으로 정해지는가?
Read
- Get record by ID or unique identifier: findUnique
// By unique identifier
const user = await prisma.user.findUnique({
where: {
email: 'elsa@prisma.io',
},
})
// By ID
const user = await prisma.user.findUnique({
where: {
id: 99,
},
})
- Get all records: findMany
const users = await prisma.user.findMany()
- Get the first record that matches a specific criteria: findFirst
const findUser = await prisma.user.findFirst({
where: {
posts: {
some: {
likes: {
gt: 100
}
}
}
},
orderBy: {
id: "desc"
}
})
}
Update
- Update a single record: update
const updateUser = await prisma.user.update({
where: {
email: 'viola@prisma.io',
},
data: {
name: 'Viola the Magnificent',
},
})
- Update multiple records: updateMany
const updateUsers = await prisma.user.updateMany({
where: {
email: {
contains: 'prisma.io',
},
},
data: {
role: 'ADMIN',
},
})
- Update or create records: upsert
const upsertUser = await prisma.user.upsert({
where: {
email: 'viola@prisma.io',
},
update: {
name: 'Viola the Magnificent',
},
create: {
email: 'viola@prisma.io',
name: 'Viola the Magnificent',
},
})
Delete
- Delete a single record: delete
const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})
- Delete multiple records: deleteMany
const deleteUsers = await prisma.user.deleteMany({
where: {
email: {
contains: 'prisma.io',
},
},
})
- Delete all records: deleteMany
const deleteUsers = await prisma.user.deleteMany({})
Select fields
Return the default selection set
쿼리의 반환값 결과에는 default selection set이 포함된다.
// Query returns User or null
const getUser: User | null = await prisma.user.findUnique({
where: {
id: 22,
},
})
Select specific fields
Select 를 사용하여 일부분만 보여줄 수 있다.
// Returns an object or null
const getUser: object | null = await prisma.user.findUnique({
where: {
id: 22,
},
select: {
email: true,
name: true,
},
})
Include relations and select relation fields
nested select 를 통해 좀 더 디테일하게 사용할 수 있다.
const users = await prisma.user.findMany({
select: {
name: true,
posts: {
select: {
title: true,
},
},
},
})
Relation queries
Prisma Client의 핵심 기능
Nested reads
- Use [include](<https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#include>) to include related records, such as a user's posts or profile, in the query response.
- Use a nested [select](<https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#select>) to include specific fields from a related record. You can also nest select inside an include.
const getUser = await prisma.user.findUnique({
where: {
id: 19,
},
include: {
posts: true,
},
})
Nested writes
- nested write creates a User and two related Post records
const createUserAndPost = await prisma.user.create({
data: {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
posts: {
create: [
{ title: 'How to make an omelette' },
{ title: 'How to eat an omelette' },
],
},
},
})
Filtering and sorting
Filtering
- Returns all User records with:
- an email address that ends with prisma.io and
- at least one published post (a relation query)
- Returns all User fields
- Includes all related Post records where published equals true
const result = await prisma.user.findMany({
where: {
email: {
endsWith: 'prisma.io',
},
posts: {
some: {
published: true,
},
},
},
include: {
posts: {
where: {
published: true,
},
},
},
})
- Combining operators (Using NOT and OR API)
const result = await prisma.user.findMany({
where: {
OR: [
{
email: {
endsWith: 'prisma.io',
},
},
{ email: { endsWith: 'gmail.com' } },
],
NOT: {
email: {
endsWith: 'hotmail.com',
},
},
},
select: {
email: true,
},
})
Sorting
- Use OrderBy API
const usersWithPosts = await prisma.user.findMany({
orderBy: [
{
role: 'desc',
},
{
name: 'desc',
},
],
include: {
posts: {
orderBy: {
title: 'desc',
},
select: {
title: true,
},
},
},
})
Pagination
Offset pagination
page 또는 offset/limit를 지정하여 데이터에서 pagination 된 결과를 조회하는 방식
const results = await prisma.post.findMany({
skip: 3,
take: 4,
})
Cursor-based pagination
사용자가 현재 조회하고 있는 id값(PK)를 기준으로 그 다음 데이터를 제공해주는 것
const firstQueryResults = await prisma.post.findMany({
take: 4,
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
orderBy: {
id: 'asc',
},
})
// Bookmark your location in the result set - in this
// case, the ID of the last post in the list of 4.
|const lastPostInResults = firstQueryResults[3] // Remember: zero-based index! :)
|const myCursor = lastPostInResults.id // Example: 29
Prisma schema
구성요소
- Data sources: Prisma를 사용하여 연결할 데이터베이스의 정보를 설정해주는 필드
- → Database의 종류(provider), Database URI(url)
- Generators: Prisma 명령을 실행하는 환경을 결정하는 필드 → provider 설정
- Data model definition: 모델과 관계를 명확하게 구분해준다.
Prisma migrate
database migration?
DB 버전 업그레이드, 노후화된 노드의 변경 및 교체 등의 이유로 운영 중인 DB를 변경해야할 때, 기존 DB에 저장되어 있던 데이터를 신규 DB로 옮기는 과정
Prisma Migrate
- Local development enviroment (Feature branch)
- Preview/staging environment (Feature pull request)
- Production (main branch)
Prisma studio
Prisma studio의 GUI 클라이언트
설치
$ npx prisma studio
'정보' 카테고리의 다른 글
[IntelliJ] 인텔리제이 새로운 UI 사용하기 (0) | 2023.07.27 |
---|---|
[macOS] 도메인 접근 허용하기 (0) | 2023.05.05 |
[graphQL] graphQL 알아보기 (0) | 2023.03.31 |
[NestJS] Failed to execute command: npm install --silent 에러 (0) | 2023.02.23 |
[Docker] 도커 알아보기 (1) | 2022.11.21 |