반응형
m.logPrint() is working!
<eq> and <eqq> tags are rendered to MathJax format, being enclosed by \ ( \ ) and \ [ \ ].
docuK1 scripts started!
If this log is not closed automatically, there must be an error somewhere in your document or scripts.
Table of Contents is filled out.
Auto numberings of sections (div.sec>h2, div.subsec>h3, div.subsubsec>h4), <eqq> tags, and <figure> tags are done.
<cite> and <refer> tags are rendered to show bubble reference.
<codeprint> tags are printed to corresponding <pre> tags, only when the tags exist in the document.
Current styles (dark/bright mode, font-family, font-size, line-height) are shown.
disqus.js with id="disqus-js" is loaded.
kakao.js with id="kakao-js-sdk" is loaded.
New ShortKeys (T: Table of Contents, F: Forward Section, D: Previous Section, L: To 전체목록/[Lists]) are set.
m.delayPad=0;
m.wait=1024;
wait 1196ms.
Doing delayed-load. : 1
Kakao.isInitialized()=true;
MathJax.js (mathjax@3.2.2/es5/tex-mml-chtml.min.js) is loaded.
Google code prettyfy.js is loaded.
docuK scripts are all done. Then this log is closing in 1.0 sec.
wait 691ms.
wait 689ms.
MathJax is loaded, but not yet initialized.
MathJax is initialized, and the initial typeset is queued.
Doing delayed-load. : 1
<eq> and <eqq> tags are rendered to MathJax format, being enclosed by \ ( \ ) and \ [ \ ].
docuK1 scripts started!
If this log is not closed automatically, there must be an error somewhere in your document or scripts.
Table of Contents is filled out.
Auto numberings of sections (div.sec>h2, div.subsec>h3, div.subsubsec>h4), <eqq> tags, and <figure> tags are done.
<cite> and <refer> tags are rendered to show bubble reference.
<codeprint> tags are printed to corresponding <pre> tags, only when the tags exist in the document.
Current styles (dark/bright mode, font-family, font-size, line-height) are shown.
disqus.js with id="disqus-js" is loaded.
kakao.js with id="kakao-js-sdk" is loaded.
New ShortKeys (T: Table of Contents, F: Forward Section, D: Previous Section, L: To 전체목록/[Lists]) are set.
m.delayPad=0;
m.wait=1024;
wait 1196ms.
Doing delayed-load. : 1
Kakao.isInitialized()=true;
MathJax.js (mathjax@3.2.2/es5/tex-mml-chtml.min.js) is loaded.
Google code prettyfy.js is loaded.
docuK scripts are all done. Then this log is closing in 1.0 sec.
wait 691ms.
wait 689ms.
MathJax is loaded, but not yet initialized.
MathJax is initialized, and the initial typeset is queued.
Doing delayed-load. : 1







Mode: Bright; Font: Noto Sans KR; font-size: 18.0px (10.0); line-height: 1.6;
width: 1280, height: 720, version: 3.0.0
Canonical URI: https://kipid.tistory.com/entry/prisma-with-PostgreSQL-를-배워봅시다
dg:plink (Document Global Permanent Link): https://kipid.tistory.com/373
document.referrer: Empty
width: 1280, height: 720, version: 3.0.0
Canonical URI: https://kipid.tistory.com/entry/prisma-with-PostgreSQL-를-배워봅시다
dg:plink (Document Global Permanent Link): https://kipid.tistory.com/373
document.referrer: Empty







prisma with PostgreSQL 를 배워봅시다.
Table of Contents
docuK0-sec-PH0.Posting History
1.Prisma 초기화
2.Schcema.prisma 설정
3.migration
4.GUI (Graphical User Interface): npx prisma studio
5.CRUD (Create Retrieve Update Delete)
5.1.Request 보내보기
6.DB seeding: npx prisma db seed
7.findMany()
7.1..findUnique(), .findUniqueOrThrow(), findFirst(), upsert(), .count()
7.1.1.AND (여러 필터 조건을 모두 만족해야 하는 경우): where 안에 프로퍼티를 여러 개 쓰면 됩니다.
7.1.2.OR (여러 필터 조건 중 하나만 만족해도 되는 경우): OR 연산자를 사용하면 됩니다.
7.1.3.NOT (필터 조건을 만족하면 안 되는 경우): NOT 연산자를 사용하면 됩니다.
7.1.4.필터 조건에 대한 자세한 내용은 다음을 참고.
8.Superstruct Types 와 Refinements
8.1.데이터를 비교할 때는 assert() 함수를 사용.
8.2.오류 처리
9.1:1 관계, 1:N 관계, N:M 관계
9.1.일대다 관계
9.2.일대일 관계
9.3.다대다 관계
9.4.최소 카디널리티
9.5.onDelete 설정하기
9.6.Create, Update connected entities/tables.
9.7.관련된 객체 연결, 연결 해제하기
10.비즈니스 로직 (Business Logic): $transaction
docuK0-sec-RRA0.References and Related Articles
T1.Prisma 초기화
▼ Show/Hide
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- npx prisma init --datasource-provider postgresql
.env
파일 설정하기. 아이디는 자동으로 .env
로 정해지고 아래에 .env
는 자신이 설정한 비밀번호를 넣어주고 (비번에 특수문자가 들어간 경우 encodeURIComponent 함수로 한번 처리해 준 뒤 넣어줘야 함. #, ? 같은게 URL 에선 특수하게 쓰이니...), .env
에는 사용할 DB name 을 입력해준다. 없는 DB 라도 prisma 가 자동으로 생성해주니 이전에 만들었던 DB 를 이용하는 경우라면 오타가 안나게 조심할 것.
macOS 서는 컴퓨터 유저 이름과 비밀번호, Windows 에서는 postgres 와 설치 시 설정한 비밀번호를 사용.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- DATABASE_URL="postgresql://postgres:[password]@localhost:5432/[database_name]?schema=public"
- PORT=3000
아래와 같은
.env
가 자동으로 생성되면 초기화 완성.Domain Specific Language: DSL (스키마 정의 전용 언어, 나름의 문법) 사용.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- // This is your Prisma schema file,
- // learn more about it in the docs: https://pris.ly/d/prisma-schema
- // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
- // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
- generator client {
- provider = "prisma-client-js"
- }
- datasource db {
- provider = "postgresql"
- url = env("DATABASE_URL")
- }
▲ Hide
T2.Schcema.prisma 설정
▼ Show/Hide
VS code 에선
.env
후 명령어로 Format Document 를 눌러서 자동 formatting 을 해주면 좋음. (바로가기 단축키는 .env
)On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- // This is your Prisma schema file,
- // learn more about it in the docs: https://pris.ly/d/prisma-schema
- generator client {
- provider = "prisma-client-js"
- }
- datasource db {
- provider = "postgresql"
- url = env("DATABASE_URL")
- }
- model User {
- id String @id @default(uuid())
- email String @unique
- firstName String
- lastName String
- address String
- age Int
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@unique([firstName, lastName]) // 여러 필드의 조합이 unique 해야 하는 경우 @@unique 어트리뷰트를 사용할 수 있습니다. @@unique 어트리뷰트는 특정 필드에 종속된 어트리뷰트가 아니기 때문에 모델 아래 부분에 씁니다.
- }
- // Int, Float, Boolean <- RDBMS
- // Nullable NULL 이 들어갈 수 있다. - 선택적 필드
- // required -> Non-nullable NULL 이 들어갈 수 없다. - 필수 필드
- enum Category {
- FASHION
- BEAUTY
- SPORTS
- ELECTRONICS
- HOME_INTERIOR
- HOUSEHOLD_SUPPLIES
- KITCHENWARE
- }
- model Product {
- id String @id @default(uuid())
- name String
- description String?
- category Category
- price Float
- stock Int
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- }
새로운 field 를 추가할 땐, @default([value]) 를 주거나 optional field (Nuallable) 로 만든 뒤 값을 수동으로 채우고 다시 required field (Non-nullable) 로 바꿔줘야 한다.
./migrations directory 에 Schema 를 바꾼 기록이 모두 남아있다. 지우지 않는것이 좋음.
▲ Hide
T3.migration
▼ Show/Hide
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- npx prisma migrate dev
Schema 를 변경한 뒤에는 항상 실행해줘야 함. Dev 환경에서만 사용하길 권함 .
▲ Hide
T4.GUI (Graphical User Interface): npx prisma studio
▼ Show/Hide
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- npx prisma studio
▲ Hide
T5.CRUD (Create Retrieve Update Delete)
▼ Show/Hide
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- import express from 'express';
- import { PrismaClient } from '@prisma/client';
- import * as dotenv from 'dotenv';
- dotenv.config();
- const prisma = new PrismaClient(); // PostgreSQL 에 접속할 수 있게 해주는 client 객체. 자동완성이 잘 되어 있으니 잘 활용할 것.
- const HttpStatus = Object.freeze({
- SUCCESS: 200,
- CREATED: 201,
- ACCEPTED: 202,
- NON_AUTHORITATIVE_INFORMATION: 203,
- NO_CONTENT: 204,
- BAD_REQUEST: 400,
- UNAUTHORIZED: 401,
- FORBIDDEN: 403,
- NOT_FOUND: 404,
- SERVER_ERROR: 500,
- });
- const app = express();
- app.use(express.json());
- const asyncHandler = (handler) => {
- return (async function (req, res) {
- try {
- await handler(req, res);
- }
- catch (err) {
- console.log(err.name);
- console.log(err.message);
- if (err.name === "ValidationError") {
- res.status(HttpStatus.BAD_REQUEST).send({message: err.message});
- }
- else if (err.name === "CastError") {
- res.status(HttpStatus.NOT_FOUND).sned({message: err.message});
- }
- else {
- res.status(HttpStatus.SERVER_ERROR).send({message: err.message});
- }
- }
- });
- };
- app.get('/users', asyncHandler(async (req, res) => {
- console.log("req.query: ", req.query);
- const { offset = 0, limit = 10, order = "newest" } = req.query || {};
- let orderBy;
- switch (order) {
- case "oldest":
- orderBy = { createdAt: 'asc' };
- break;
- case "newest":
- default:
- orderBy = { createdAt: 'desc' };
- }
- const users = await prisma.user.findMany({
- orderBy,
- skip: parseInt(offset),
- take: parseInt(limit),
- });
- res.send(users);
- }));
- app.get('/users/:id', asyncHandler(async (req, res) => {
- const { id } = req.params;
- const user = await prisma.user.findUnique({
- where: { id },
- });
- res.send(user);
- }));
- app.post('/users', asyncHandler(async (req, res) => {
- const user = await prisma.user.create({ data: req.body });
- res.status(201).send(user);
- }));
- app.patch('/users/:id', asyncHandler(async (req, res) => {
- const { id } = req.params;
- const user = await prisma.user.update({
- where: { id },
- data: req.body,
- });
- await user.update(req.body);
- res.send(user);
- }));
- app.patch('/users', asyncHandler(async (req, res) => {
- const users = await prisma.user.update({
- where: req.params,
- data: req.body,
- });
- await users.update(req.body);
- res.send(users);
- }));
- app.delete('/users/:id', asyncHandler(async (req, res) => {
- const { id } = req.params;
- await prisma.user.delete({
- where: { id },
- });
- res.sendStatus(204);
- }));
- /*********** products ***********/
- app.get('/products', asyncHandler(async (req, res) => {
- const { offset = 0, limit = 10, order = "newest", category } = req.query;
- let orderBy;
- switch (order) {
- case "oldest":
- orderBy = { createdAt: 'asc' };
- break;
- case "newest":
- default:
- orderBy = { createdAt: 'desc' };
- }
- const products = await prisma.product.findMany({
- orderBy,
- skip: parseInt(offset),
- take: parseInt(limit),
- where: category ? { category } : {},
- });
- res.send(products);
- }));
- app.get('/products/:id', asyncHandler(async (req, res) => {
- const { id } = req.params;
- const product = await prisma.product.findUnique({
- where: { id },
- });
- res.send(product);
- }));
- app.post('/products', asyncHandler(async (req, res) => {
- const product = await prisma.product.create({ data: req.body });
- res.status(201).send(product);
- }));
- app.patch('/products/:id', asyncHandler(async (req, res) => {
- const { id } = req.params;
- const product = await prisma.product.update({
- where: { id },
- data: req.body,
- });
- res.send(product);
- }));
- app.delete('/products/:id', asyncHandler(async (req, res) => {
- const { id } = req.params;
- await prisma.product.delete({
- where: { id },
- });
- res.sendStatus(204);
- }));
- app.listen(process.env.PORT || 3000, () => console.log('Server Started'));
T5.1.Request 보내보기
VS code extension 중 뭘 깔아야 이게 실행되는 거였더라? (REST Client
[04]
인가?) =ㅇ=;; 아무튼 다음과 같은 .env
파일을 만들면 CRUD 를 테스트 할 수 있음.On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- GET http://localhost:3000/users
- ###
- GET http://localhost:3000/users?order=newest&offset=1&limi=15
- ###
- GET http://localhost:3000/users/6f3182a9-c20b-4c8b-aefd-c1b2f2fc35d5
- ###
- POST http://localhost:3000/users
- Content-Type: application/json
- {
- "email": "yjkim@example.com",
- "firstName": "유진",
- "lastName": "김",
- "address": "충청북도 청주시 북문로 210번길 5",
- "age": 23
- }
- ###
- PATCH http://localhost:3000/users/6f3182a9-c20b-4c8b-aefd-c1b2f2fc35d5
- Content-Type: application/json
- {
- "address": "서울특별시 강남구 무실로 234번길 45-6"
- }
- ###
- PATCH http://localhost:3000/users?order=newest
- Content-Type: application/json
- {
- "address": "서울특별시 강남구 무실로 234번길 45-6"
- }
- ###
- DELETE http://localhost:3000/users/6f3182a9-c20b-4c8b-aefd-c1b2f2fc35d5
▲ Hide
T6.DB seeding: npx prisma db seed
▼ Show/Hide
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- {
- "dependencies": {
- "@prisma/client": "^5.19.1",
- "dotenv": "^16.3.1",
- "express": "^4.18.2",
- "is-email": "^1.0.2",
- "is-uuid": "^1.0.2",
- "superstruct": "^1.0.3"
- },
- "devDependencies": {
- "nodemon": "^3.0.1",
- "prisma": "^5.19.1"
- },
- "type": "module",
- "scripts": {
- "dev": "nodemon app.js",
- "start": "node app.js"
- },
- "prisma": {
- "seed": "node prisma/seed.js"
- }
- }
와 같이 package.json 을 설정해 주고.
prisma/seed.js file 을 다음과 같이 만들어주자.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- import { PrismaClient } from '@prisma/client';
- import { USERS, PRODUCTS } from './mock.js';
- const prisma = new PrismaClient();
- async function main() {
- await prisma.user.deleteMany();
- await prisma.user.createMany({
- data: USERS,
- skipDuplicates: true,
- });
- await prisma.product.deleteMany();
- await prisma.product.createMany({
- data: PRODUCTS,
- skipDuplicates: true,
- });
- }
- main()
- .then(async () => {
- await prisma.$disconnect();
- })
- .catch(async (e) => {
- console.error(e);
- await prisma.$disconnect();
- process.exit(1);
- });
그러면 다음과 같은 명령어로 DB seeding 을 할 수 있다.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- npx prisma db seed
▲ Hide
T7.findMany()
▼ Show/Hide
다음과 같은 설정들을 사용할 수 있다.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- function asyncHandler(handler) {
- return async function (req, res) {
- try {
- await handler(req, res);
- } catch (e) {
- if (
- e.name === 'StructError' ||
- e instanceof Prisma.PrismaClientValidationError
- ) {
- res.status(400).send({ message: e.message });
- } else if (
- e instanceof Prisma.PrismaClientKnownRequestError &&
- e.code === 'P2025'
- ) {
- res.sendStatus(404);
- } else {
- res.status(500).send({ message: e.message });
- }
- }
- };
- }
- /*********** users ***********/
- app.get('/users', asyncHandler(async (req, res) => {
- const { offset = 0, limit = 10, order = 'newest' } = req.query;
- let orderBy;
- switch (order) {
- case 'oldest':
- orderBy = { createdAt: 'asc' };
- break;
- case 'newest':
- default:
- orderBy = { createdAt: 'desc' };
- }
- const users = await prisma.user.findMany({
- orderBy,
- skip: parseInt(offset),
- take: parseInt(limit),
- // include: {
- // userPreference: {
- // select: {
- // receiveEmail: true,
- // }
- // }
- // },
- select: {
- email: true,
- userPreference: {
- select: {
- receiveEmail: true,
- }
- },
- },
- });
- res.send(users);
- }));
include 와 select 는 동시에 못쓴다고 하니 주의.
T7.1..findUnique(), .findUniqueOrThrow(), findFirst(), upsert(), .count()
Client method 는 참조.
not, in, contains, startsWith 같은 다양한 비교 연산자 사용 가능.
T7.1.1.AND (여러 필터 조건을 모두 만족해야 하는 경우): where 안에 프로퍼티를 여러 개 쓰면 됩니다.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- // category가 'FASHION'이면서 name에 '나이키'가 들어가는 Product들 필터
- const products = await prisma.product.findMany({
- where: {
- category: 'FASHION',
- name: {
- contains: '나이키',
- },
- },
- });
T7.1.2.OR (여러 필터 조건 중 하나만 만족해도 되는 경우): OR 연산자를 사용하면 됩니다.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- // name에 '아디다스'가 들어가거나 '나이키'가 들어가거는 Product들 필터
- const products = await prisma.product.findMany({
- where: {
- OR: [
- {
- name: {
- contains: '아디다스',
- },
- },
- {
- name: {
- contains: '나이키',
- },
- },
- ],
- },
- });
T7.1.3.NOT (필터 조건을 만족하면 안 되는 경우): NOT 연산자를 사용하면 됩니다.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- // name에 '삼성'이 들어가지만 'TV'는 들어가지 않는 Product들 필터
- const products = await prisma.product.findMany({
- where: {
- name: {
- contains: '삼성',
- },
- NOT: {
- name: {
- contains: 'TV',
- },
- },
- },
- });
▲ Hide
T8.Superstruct Types 와 Refinements
▼ Show/Hide
참조: Types
[07]
, Refinements [08]
아래와 같이 작성.
superstruct 라이브러리의 .string(), .number(), .integer(), .boolean(), .define(), .object(), .enums(), .array(), .partial() 타입들로 틀을 정의하고 .size(), .min(), .max() 함수로 제약을 추가.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- import * as s from 'superstruct';
- import isEmail from 'is-email';
- import isUuid from 'is-uuid';
- const CATEGORIES = [
- 'FASHION',
- 'BEAUTY',
- 'SPORTS',
- 'ELECTRONICS',
- 'HOME_INTERIOR',
- 'HOUSEHOLD_SUPPLIES',
- 'KITCHENWARE',
- ];
- const STATUSES = ['PENDING', 'COMPLETE'];
- const Uuid = s.define('Uuid', (value) => isUuid.v4(value));
- export const CreateUser = s.object({
- email: s.define('Email', isEmail),
- firstName: s.size(s.string(), 1, 30),
- lastName: s.size(s.string(), 1, 30),
- address: s.string(),
- userPreference: s.object({
- receiveEmail: s.boolean(),
- }),
- });
- export const PatchUser = s.partial(CreateUser);
- export const CreateProduct = s.object({
- name: s.size(s.string(), 1, 60),
- description: s.string(),
- category: s.enums(CATEGORIES),
- price: s.min(s.number(), 0),
- stock: s.min(s.integer(), 0),
- });
- export const PatchProduct = s.partial(CreateProduct);
- export const CreateOrder = s.object({
- userId: Uuid,
- orderItems: s.size(
- s.array(
- s.object({
- productId: Uuid,
- unitPrice: s.min(s.number(), 0),
- quantity: s.min(s.integer(), 1),
- })
- ),
- 1,
- Infinity
- ),
- });
- export const PatchOrder = s.object({
- status: s.enums(STATUSES),
- });
- export const CreateSavedProducts = s.object({
- });
T8.1.데이터를 비교할 때는 assert() 함수를 사용.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- import { assert } from 'superstruct';
- import { CreateUser } from './structs.js';
- // ...
- app.post('/users', async (req, res) => {
- assert(req.body, CreateUser); // CreateUser 형식이 아니라면 오류 발생
- // ...
- });
T8.2.오류 처리
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- import { PrismaClient, Prisma } from '@prisma/client';
- // ...
- function asyncHandler(handler) {
- return async function (req, res) {
- try {
- await handler(req, res);
- } catch (e) {
- if (
- e instanceof Prisma.PrismaClientValidationError ||
- e.name === 'StructError'
- ) {
- res.status(400).send({ message: e.message });
- } else if (
- e instanceof Prisma.PrismaClientKnownRequestError &&
- e.code === 'P2025'
- ) {
- res.sendStatus(404);
- } else {
- res.status(500).send({ message: e.message });
- }
- }
- };
- }
- // ...
- app.post('/users', asyncHandler(async (req, res) => {
- assert(req.body, CreateUser);
- // ...
- }));
e.name === 'StructError': Superstruct 객체와 형식이 다를 경우 발생
e instanceof Prisma.PrismaClientValidationError: 데이터를 저장할 때 모델에 정의된 형식과 다른 경우 발생 (Superstruct로 철저히 검사하면 이 상황은 잘 발생하지 않지만 안전성을 위해 둘 다 검사)
e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2025': 객체를 찾을 수 없을 경우 발생
▲ Hide
T9.1:1 관계, 1:N 관계, N:M 관계
▼ Show/Hide
T9.1.일대다 관계
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- model User {
- // ...
- orders Order[]
- }
- model Order {
- // ...
- user User @relation(fields: [userId], references: [id])
- userId String
- }
T9.2.일대일 관계
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- model User {
- // ...
- userPreference UserPreference?
- }
- model UserPreference {
- // ...
- user User @relation(fields: [userId], references: [id])
- userId String @unique
- }
T9.3.다대다 관계
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- model User {
- // ...
- savedProducts Product[]
- }
- model Product {
- // ...
- savedUsers User[]
- }
T9.4.최소 카디널리티
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- model User {
- // ...
- orders Order[]
- }
- model Order {
- // ...
- user User @relation(fields: [userId], references: [id])
- userId String
- }
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- model User {
- // ...
- orders Order[]
- }
- model Order {
- // ...
- user User? @relation(fields: [userId], references: [id])
- userId String?
- }
T9.5.onDelete 설정하기
참조:
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- model Order {
- // ...
- user User @relation(fields: [userId], references: [id], onDelete: ...)
- userId String
- }
Cascade: userId 가 가리키는 유저가 삭제되면 기존 데이터도 삭제됩니다.
Restrict: userId 를 통해 유저를 참조하는 주문이 하나라도 있다면 유저를 삭제할 수 없습니다.
SetNull: userId 가 가리키는 유저가 삭제되면 userId 를 NULL 로 설정합니다. user 와 userId 모두 옵셔널해야 합니다.
SetDefault: userId 가 가리키는 유저가 삭제되면 userId 를 디폴트 값으로 설정합니다. userId 필드에 @default()를 제공해야 합니다.
관계 필드와 foreign key 가 필수일 경우 Restrict 가 기본값이고 옵셔널할 경우 SetNull 이 기본값입니다.
T9.6.Create, Update connected entities/tables.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- /* create */
- const postBody = {
- email: 'yjkim@example.com',
- firstName: '유진',
- lastName: '김',
- address: '충청북도 청주시 북문로 210번길 5',
- userPreference: {
- receiveEmail: false,
- },
- };
- const { userPreference, ...userFields } = postBody;
- const user = await prisma.user.create({
- data: {
- ...userFields,
- userPreference: {
- create: userPreference,
- },
- },
- include: {
- userPreference: true,
- },
- });
- console.log(user);
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- /* update */
- const id = 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e';
- const patchBody = {
- email: 'honggd2@example.com',
- userPreference: {
- receiveEmail: false,
- },
- };
- const { userPreference, ...userFields } = patchBody;
- const user = await prisma.user.update({
- where: { id },
- data: {
- ...userFields,
- userPreference: {
- update: userPreference,
- },
- },
- include: {
- userPreference: true,
- },
- });
- console.log(user);
T9.7.관련된 객체 연결, 연결 해제하기
관련된 객체 연결.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- const userId = 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e';
- const productId = 'c28a2eaf-4d87-4f9f-ae5b-cbcf73e24253';
- const user = await prisma.user.update({
- where: { id: userId },
- data: {
- savedProducts: {
- connect: {
- id: productId,
- },
- },
- },
- include: {
- savedProducts: true,
- },
- });
- console.log(user);
관련된 객체 연결 해제.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- const userId = 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e';
- const productId = 'c28a2eaf-4d87-4f9f-ae5b-cbcf73e24253';
- const user = await prisma.user.update({
- where: { id: userId },
- data: {
- savedProducts: {
- disconnect: {
- id: productId,
- },
- },
- },
- include: {
- savedProducts: true,
- },
- });
- console.log(user);
종합
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- app.patch('/users/:id/saved-products', asyncHandler(async (req, res) => {
- assert(req.body, PostSavedProducts);
- const { id: userId } = req.params;
- const { productId } = req.body;
- const user = await prisma.user.update({
- where: { id: userId },
- data: {
- savedProducts: {
- connect: {
- id: productId,
- },
- },
- },
- include: {
- savedProducts: true,
- },
- });
- res.send(user);
- }));
- app.patch('/users/:id/unsave-product', asyncHandler(async (req, res) => {
- assert(req.body, PostSavedProducts);
- const { id: userId } = req.params;
- const { productId } = req.body;
- const user = await prisma.user.update({
- where: { id: userId },
- data: {
- savedProducts: {
- disconnect: {
- id: productId,
- },
- },
- },
- include: {
- savedProducts: true,
- },
- });
- res.send(user);
- }));
▲ Hide
T10.비즈니스 로직 (Business Logic): $transaction
▼ Show/Hide
Order 와 product stock 과의 로직 완성하기. $transaction: Do All or Nothing.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
- app.post('/orders', asyncHandler(async (req, res) => {
- assert(req.body, CreateOrder);
- const { userId, orderItems } = req.body;
- const productIds = orderItems.map((orderItem) => orderItem.productId);
- const products = await prisma.product.findMany({
- where: { id: { in: productIds } },
- });
- function getQuantity(productId) {
- const orderItem = orderItems.find(
- (orderItem) => orderItem.productId === productId
- );
- return orderItem.quantity;
- }
- // 재고 확인
- const isSufficientStock = products.every((product) => {
- const { id, stock } = product;
- return stock >= getQuantity(id);
- });
- if (!isSufficientStock) {
- throw new Error('Insufficient Stock');
- }
- const [order] = await prisma.$transaction([
- prisma.order.create({
- data: {
- userId,
- orderItems: {
- create: orderItems,
- },
- },
- include: {
- orderItems: true,
- },
- }),
- ...orderItems.map(({ productId, quantity }) => {
- return prisma.product.update({
- where: { id: productId },
- data: {
- stock: {
- decrement: quantity,
- },
- },
- });
- })
- ]);
- res.status(201).send(order);
- }));
▲ Hide
TdocuK0-sec-RRA0.References and Related Articles
▼ Show/Hide
- Ref. [01] 관계형 데이터베이스를 활용한 자바스크립트 서버 만들기 - 코드잇
- Ref. [02] kipid's blog :: Encode/Unescape and Decode/Escape URI Component
- Ref. [03] https://www.prisma.io/docs/orm/prisma-migrate/workflows/development-and-production
- Ref. [04] https://github.com/Huachao/vscode-restclient
- Ref. [05] https://www.prisma.io/docs/orm/reference/prisma-client-reference#model-queries
- Ref. [06] https://www.prisma.io/docs/orm/reference/prisma-client-reference#filter-conditions-and-operators
- Ref. [07] https://docs.superstructjs.org/api-reference/types
- Ref. [08] https://docs.superstructjs.org/api-reference/refinements
- Ref. [09] https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/referential-actions
▲ Hide







반응형