본문 바로가기

[IT/Programming]/Algorithm/Database

prisma with PostgreSQL 를 배워봅시다.

반응형
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
▼ Hide
Toggle a mess
Go (FS)
TofC
DocuK Log
Backward
Forward
RRA
Lists
CmtZ
CmtX
Handle CmtZ
Log in
out focus
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
This document is rendered by docuK (See also SEE (Super Easy Edit) of docuK and pure SEE).

prisma with PostgreSQL 를 배워봅시다.

TdocuK0-sec-PH0.Posting History

▼ Show/Hide

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}).
  1. 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}).
  1. DATABASE_URL="postgresql://postgres:[password]@localhost:5432/[database_name]?schema=public"
  2. 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}).
  1. // This is your Prisma schema file,
  2. // learn more about it in the docs: https://pris.ly/d/prisma-schema
  3.  
  4. // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
  5. // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
  6.  
  7. generator client {
  8. provider = "prisma-client-js"
  9. }
  10.  
  11. datasource db {
  12. provider = "postgresql"
  13. url = env("DATABASE_URL")
  14. }
▲ 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}).
  1. // This is your Prisma schema file,
  2. // learn more about it in the docs: https://pris.ly/d/prisma-schema
  3.  
  4. generator client {
  5. provider = "prisma-client-js"
  6. }
  7.  
  8. datasource db {
  9. provider = "postgresql"
  10. url = env("DATABASE_URL")
  11. }
  12.  
  13. model User {
  14. id String @id @default(uuid())
  15. email String @unique
  16. firstName String
  17. lastName String
  18. address String
  19. age Int
  20. createdAt DateTime @default(now())
  21. updatedAt DateTime @updatedAt
  22.  
  23. @@unique([firstName, lastName]) // 여러 필드의 조합이 unique 해야 하는 경우 @@unique 어트리뷰트를 사용할 수 있습니다. @@unique 어트리뷰트는 특정 필드에 종속된 어트리뷰트가 아니기 때문에 모델 아래 부분에 씁니다.
  24. }
  25.  
  26. // Int, Float, Boolean <- RDBMS
  27. // Nullable NULL 이 들어갈 수 있다. - 선택적 필드
  28. // required -> Non-nullable NULL 이 들어갈 수 없다. - 필수 필드
  29.  
  30. enum Category {
  31. FASHION
  32. BEAUTY
  33. SPORTS
  34. ELECTRONICS
  35. HOME_INTERIOR
  36. HOUSEHOLD_SUPPLIES
  37. KITCHENWARE
  38. }
  39.  
  40. model Product {
  41. id String @id @default(uuid())
  42. name String
  43. description String?
  44. category Category
  45. price Float
  46. stock Int
  47. createdAt DateTime @default(now())
  48. updatedAt DateTime @updatedAt
  49. }
새로운 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}).
  1. 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}).
  1. 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}).
  1. import express from 'express';
  2. import { PrismaClient } from '@prisma/client';
  3. import * as dotenv from 'dotenv';
  4. dotenv.config();
  5.  
  6. const prisma = new PrismaClient(); // PostgreSQL 에 접속할 수 있게 해주는 client 객체. 자동완성이 잘 되어 있으니 잘 활용할 것.
  7.  
  8. const HttpStatus = Object.freeze({
  9. SUCCESS: 200,
  10. CREATED: 201,
  11. ACCEPTED: 202,
  12. NON_AUTHORITATIVE_INFORMATION: 203,
  13. NO_CONTENT: 204,
  14. BAD_REQUEST: 400,
  15. UNAUTHORIZED: 401,
  16. FORBIDDEN: 403,
  17. NOT_FOUND: 404,
  18. SERVER_ERROR: 500,
  19. });
  20.  
  21. const app = express();
  22. app.use(express.json());
  23.  
  24. const asyncHandler = (handler) => {
  25. return (async function (req, res) {
  26. try {
  27. await handler(req, res);
  28. }
  29. catch (err) {
  30. console.log(err.name);
  31. console.log(err.message);
  32. if (err.name === "ValidationError") {
  33. res.status(HttpStatus.BAD_REQUEST).send({message: err.message});
  34. }
  35. else if (err.name === "CastError") {
  36. res.status(HttpStatus.NOT_FOUND).sned({message: err.message});
  37. }
  38. else {
  39. res.status(HttpStatus.SERVER_ERROR).send({message: err.message});
  40. }
  41. }
  42. });
  43. };
  44.  
  45. app.get('/users', asyncHandler(async (req, res) => {
  46. console.log("req.query: ", req.query);
  47. const { offset = 0, limit = 10, order = "newest" } = req.query || {};
  48. let orderBy;
  49. switch (order) {
  50. case "oldest":
  51. orderBy = { createdAt: 'asc' };
  52. break;
  53. case "newest":
  54. default:
  55. orderBy = { createdAt: 'desc' };
  56. }
  57. const users = await prisma.user.findMany({
  58. orderBy,
  59. skip: parseInt(offset),
  60. take: parseInt(limit),
  61. });
  62. res.send(users);
  63. }));
  64.  
  65. app.get('/users/:id', asyncHandler(async (req, res) => {
  66. const { id } = req.params;
  67. const user = await prisma.user.findUnique({
  68. where: { id },
  69. });
  70. res.send(user);
  71. }));
  72.  
  73. app.post('/users', asyncHandler(async (req, res) => {
  74. const user = await prisma.user.create({ data: req.body });
  75. res.status(201).send(user);
  76. }));
  77.  
  78. app.patch('/users/:id', asyncHandler(async (req, res) => {
  79. const { id } = req.params;
  80. const user = await prisma.user.update({
  81. where: { id },
  82. data: req.body,
  83. });
  84. await user.update(req.body);
  85. res.send(user);
  86. }));
  87.  
  88. app.patch('/users', asyncHandler(async (req, res) => {
  89. const users = await prisma.user.update({
  90. where: req.params,
  91. data: req.body,
  92. });
  93. await users.update(req.body);
  94. res.send(users);
  95. }));
  96.  
  97. app.delete('/users/:id', asyncHandler(async (req, res) => {
  98. const { id } = req.params;
  99. await prisma.user.delete({
  100. where: { id },
  101. });
  102. res.sendStatus(204);
  103. }));
  104.  
  105. /*********** products ***********/
  106.  
  107. app.get('/products', asyncHandler(async (req, res) => {
  108. const { offset = 0, limit = 10, order = "newest", category } = req.query;
  109. let orderBy;
  110. switch (order) {
  111. case "oldest":
  112. orderBy = { createdAt: 'asc' };
  113. break;
  114. case "newest":
  115. default:
  116. orderBy = { createdAt: 'desc' };
  117. }
  118.  
  119. const products = await prisma.product.findMany({
  120. orderBy,
  121. skip: parseInt(offset),
  122. take: parseInt(limit),
  123. where: category ? { category } : {},
  124. });
  125. res.send(products);
  126. }));
  127.  
  128. app.get('/products/:id', asyncHandler(async (req, res) => {
  129. const { id } = req.params;
  130. const product = await prisma.product.findUnique({
  131. where: { id },
  132. });
  133. res.send(product);
  134. }));
  135.  
  136. app.post('/products', asyncHandler(async (req, res) => {
  137. const product = await prisma.product.create({ data: req.body });
  138. res.status(201).send(product);
  139. }));
  140.  
  141. app.patch('/products/:id', asyncHandler(async (req, res) => {
  142. const { id } = req.params;
  143. const product = await prisma.product.update({
  144. where: { id },
  145. data: req.body,
  146. });
  147. res.send(product);
  148. }));
  149.  
  150. app.delete('/products/:id', asyncHandler(async (req, res) => {
  151. const { id } = req.params;
  152. await prisma.product.delete({
  153. where: { id },
  154. });
  155. res.sendStatus(204);
  156. }));
  157.  
  158. app.listen(process.env.PORT || 3000, () => console.log('Server Started'));

T5.1.Request 보내보기

VS code extension 중 뭘 깔아야 이게 실행되는 거였더라? (REST Client 인가?) =ㅇ=;; 아무튼 다음과 같은 .env 파일을 만들면 CRUD 를 테스트 할 수 있음.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. GET http://localhost:3000/users
  2. ###
  3.  
  4. GET http://localhost:3000/users?order=newest&offset=1&limi=15
  5.  
  6. ###
  7.  
  8. GET http://localhost:3000/users/6f3182a9-c20b-4c8b-aefd-c1b2f2fc35d5
  9.  
  10. ###
  11.  
  12. POST http://localhost:3000/users
  13. Content-Type: application/json
  14.  
  15. {
  16. "email": "yjkim@example.com",
  17. "firstName": "유진",
  18. "lastName": "김",
  19. "address": "충청북도 청주시 북문로 210번길 5",
  20. "age": 23
  21. }
  22.  
  23. ###
  24.  
  25. PATCH http://localhost:3000/users/6f3182a9-c20b-4c8b-aefd-c1b2f2fc35d5
  26. Content-Type: application/json
  27.  
  28. {
  29. "address": "서울특별시 강남구 무실로 234번길 45-6"
  30. }
  31.  
  32. ###
  33.  
  34. PATCH http://localhost:3000/users?order=newest
  35. Content-Type: application/json
  36.  
  37. {
  38. "address": "서울특별시 강남구 무실로 234번길 45-6"
  39. }
  40.  
  41. ###
  42.  
  43. 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}).
  1. {
  2. "dependencies": {
  3. "@prisma/client": "^5.19.1",
  4. "dotenv": "^16.3.1",
  5. "express": "^4.18.2",
  6. "is-email": "^1.0.2",
  7. "is-uuid": "^1.0.2",
  8. "superstruct": "^1.0.3"
  9. },
  10. "devDependencies": {
  11. "nodemon": "^3.0.1",
  12. "prisma": "^5.19.1"
  13. },
  14. "type": "module",
  15. "scripts": {
  16. "dev": "nodemon app.js",
  17. "start": "node app.js"
  18. },
  19. "prisma": {
  20. "seed": "node prisma/seed.js"
  21. }
  22. }
와 같이 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}).
  1. import { PrismaClient } from '@prisma/client';
  2. import { USERS, PRODUCTS } from './mock.js';
  3.  
  4. const prisma = new PrismaClient();
  5.  
  6. async function main() {
  7. await prisma.user.deleteMany();
  8. await prisma.user.createMany({
  9. data: USERS,
  10. skipDuplicates: true,
  11. });
  12.  
  13. await prisma.product.deleteMany();
  14. await prisma.product.createMany({
  15. data: PRODUCTS,
  16. skipDuplicates: true,
  17. });
  18. }
  19.  
  20. main()
  21. .then(async () => {
  22. await prisma.$disconnect();
  23. })
  24. .catch(async (e) => {
  25. console.error(e);
  26. await prisma.$disconnect();
  27. process.exit(1);
  28. });
그러면 다음과 같은 명령어로 DB seeding 을 할 수 있다.
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. 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}).
  1. function asyncHandler(handler) {
  2. return async function (req, res) {
  3. try {
  4. await handler(req, res);
  5. } catch (e) {
  6. if (
  7. e.name === 'StructError' ||
  8. e instanceof Prisma.PrismaClientValidationError
  9. ) {
  10. res.status(400).send({ message: e.message });
  11. } else if (
  12. e instanceof Prisma.PrismaClientKnownRequestError &&
  13. e.code === 'P2025'
  14. ) {
  15. res.sendStatus(404);
  16. } else {
  17. res.status(500).send({ message: e.message });
  18. }
  19. }
  20. };
  21. }
  22.  
  23. /*********** users ***********/
  24.  
  25. app.get('/users', asyncHandler(async (req, res) => {
  26. const { offset = 0, limit = 10, order = 'newest' } = req.query;
  27. let orderBy;
  28. switch (order) {
  29. case 'oldest':
  30. orderBy = { createdAt: 'asc' };
  31. break;
  32. case 'newest':
  33. default:
  34. orderBy = { createdAt: 'desc' };
  35. }
  36. const users = await prisma.user.findMany({
  37. orderBy,
  38. skip: parseInt(offset),
  39. take: parseInt(limit),
  40. // include: {
  41. // userPreference: {
  42. // select: {
  43. // receiveEmail: true,
  44. // }
  45. // }
  46. // },
  47. select: {
  48. email: true,
  49. userPreference: {
  50. select: {
  51. receiveEmail: true,
  52. }
  53. },
  54. },
  55. });
  56. res.send(users);
  57. }));
include 와 select 는 동시에 못쓴다고 하니 주의.

T7.1..findUnique(), .findUniqueOrThrow(), findFirst(), upsert(), .count()

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}).
  1. // category가 'FASHION'이면서 name에 '나이키'가 들어가는 Product들 필터
  2. const products = await prisma.product.findMany({
  3. where: {
  4. category: 'FASHION',
  5. name: {
  6. contains: '나이키',
  7. },
  8. },
  9. });

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}).
  1. // name에 '아디다스'가 들어가거나 '나이키'가 들어가거는 Product들 필터
  2. const products = await prisma.product.findMany({
  3. where: {
  4. OR: [
  5. {
  6. name: {
  7. contains: '아디다스',
  8. },
  9. },
  10. {
  11. name: {
  12. contains: '나이키',
  13. },
  14. },
  15. ],
  16. },
  17. });

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}).
  1. // name에 '삼성'이 들어가지만 'TV'는 들어가지 않는 Product들 필터
  2. const products = await prisma.product.findMany({
  3. where: {
  4. name: {
  5. contains: '삼성',
  6. },
  7. NOT: {
  8. name: {
  9. contains: 'TV',
  10. },
  11. },
  12. },
  13. });

T7.1.4.필터 조건에 대한 자세한 내용은 다음을 참고.

▲ Hide

T8.Superstruct Types 와 Refinements

▼ Show/Hide
아래와 같이 작성.
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}).
  1. import * as s from 'superstruct';
  2. import isEmail from 'is-email';
  3. import isUuid from 'is-uuid';
  4.  
  5. const CATEGORIES = [
  6. 'FASHION',
  7. 'BEAUTY',
  8. 'SPORTS',
  9. 'ELECTRONICS',
  10. 'HOME_INTERIOR',
  11. 'HOUSEHOLD_SUPPLIES',
  12. 'KITCHENWARE',
  13. ];
  14.  
  15. const STATUSES = ['PENDING', 'COMPLETE'];
  16.  
  17. const Uuid = s.define('Uuid', (value) => isUuid.v4(value));
  18.  
  19. export const CreateUser = s.object({
  20. email: s.define('Email', isEmail),
  21. firstName: s.size(s.string(), 1, 30),
  22. lastName: s.size(s.string(), 1, 30),
  23. address: s.string(),
  24. userPreference: s.object({
  25. receiveEmail: s.boolean(),
  26. }),
  27. });
  28.  
  29. export const PatchUser = s.partial(CreateUser);
  30.  
  31. export const CreateProduct = s.object({
  32. name: s.size(s.string(), 1, 60),
  33. description: s.string(),
  34. category: s.enums(CATEGORIES),
  35. price: s.min(s.number(), 0),
  36. stock: s.min(s.integer(), 0),
  37. });
  38.  
  39. export const PatchProduct = s.partial(CreateProduct);
  40.  
  41. export const CreateOrder = s.object({
  42. userId: Uuid,
  43. orderItems: s.size(
  44. s.array(
  45. s.object({
  46. productId: Uuid,
  47. unitPrice: s.min(s.number(), 0),
  48. quantity: s.min(s.integer(), 1),
  49. })
  50. ),
  51. 1,
  52. Infinity
  53. ),
  54. });
  55.  
  56. export const PatchOrder = s.object({
  57. status: s.enums(STATUSES),
  58. });
  59.  
  60. export const CreateSavedProducts = s.object({
  61.  
  62. });

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}).
  1. import { assert } from 'superstruct';
  2. import { CreateUser } from './structs.js';
  3.  
  4. // ...
  5.  
  6. app.post('/users', async (req, res) => {
  7. assert(req.body, CreateUser); // CreateUser 형식이 아니라면 오류 발생
  8. // ...
  9. });

T8.2.오류 처리

On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. import { PrismaClient, Prisma } from '@prisma/client';
  2.  
  3. // ...
  4.  
  5. function asyncHandler(handler) {
  6. return async function (req, res) {
  7. try {
  8. await handler(req, res);
  9. } catch (e) {
  10. if (
  11. e instanceof Prisma.PrismaClientValidationError ||
  12. e.name === 'StructError'
  13. ) {
  14. res.status(400).send({ message: e.message });
  15. } else if (
  16. e instanceof Prisma.PrismaClientKnownRequestError &&
  17. e.code === 'P2025'
  18. ) {
  19. res.sendStatus(404);
  20. } else {
  21. res.status(500).send({ message: e.message });
  22. }
  23. }
  24. };
  25. }
  26.  
  27. // ...
  28.  
  29. app.post('/users', asyncHandler(async (req, res) => {
  30. assert(req.body, CreateUser);
  31. // ...
  32. }));
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}).
  1. model User {
  2. // ...
  3. orders Order[]
  4. }
  5.  
  6. model Order {
  7. // ...
  8. user User @relation(fields: [userId], references: [id])
  9. userId String
  10. }

T9.2.일대일 관계

On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. model User {
  2. // ...
  3. userPreference UserPreference?
  4. }
  5.  
  6. model UserPreference {
  7. // ...
  8. user User @relation(fields: [userId], references: [id])
  9. userId String @unique
  10. }

T9.3.다대다 관계

On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. model User {
  2. // ...
  3. savedProducts Product[]
  4. }
  5.  
  6. model Product {
  7. // ...
  8. savedUsers User[]
  9. }

T9.4.최소 카디널리티

On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. model User {
  2. // ...
  3. orders Order[]
  4. }
  5.  
  6. model Order {
  7. // ...
  8. user User @relation(fields: [userId], references: [id])
  9. userId String
  10. }
On the left side of codes is there a hiden button to toggle/switch scrollability ({max-height:some} or {max-height:none}).
  1. model User {
  2. // ...
  3. orders Order[]
  4. }
  5.  
  6. model Order {
  7. // ...
  8. user User? @relation(fields: [userId], references: [id])
  9. userId String?
  10. }

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}).
  1. model Order {
  2. // ...
  3. user User @relation(fields: [userId], references: [id], onDelete: ...)
  4. userId String
  5. }
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}).
  1. /* create */
  2.  
  3. const postBody = {
  4. email: 'yjkim@example.com',
  5. firstName: '유진',
  6. lastName: '김',
  7. address: '충청북도 청주시 북문로 210번길 5',
  8. userPreference: {
  9. receiveEmail: false,
  10. },
  11. };
  12.  
  13. const { userPreference, ...userFields } = postBody;
  14.  
  15. const user = await prisma.user.create({
  16. data: {
  17. ...userFields,
  18. userPreference: {
  19. create: userPreference,
  20. },
  21. },
  22. include: {
  23. userPreference: true,
  24. },
  25. });
  26.  
  27. 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}).
  1. /* update */
  2.  
  3. const id = 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e';
  4.  
  5. const patchBody = {
  6. email: 'honggd2@example.com',
  7. userPreference: {
  8. receiveEmail: false,
  9. },
  10. };
  11.  
  12. const { userPreference, ...userFields } = patchBody;
  13.  
  14. const user = await prisma.user.update({
  15. where: { id },
  16. data: {
  17. ...userFields,
  18. userPreference: {
  19. update: userPreference,
  20. },
  21. },
  22. include: {
  23. userPreference: true,
  24. },
  25. });
  26.  
  27. 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}).
  1. const userId = 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e';
  2. const productId = 'c28a2eaf-4d87-4f9f-ae5b-cbcf73e24253';
  3.  
  4. const user = await prisma.user.update({
  5. where: { id: userId },
  6. data: {
  7. savedProducts: {
  8. connect: {
  9. id: productId,
  10. },
  11. },
  12. },
  13. include: {
  14. savedProducts: true,
  15. },
  16. });
  17.  
  18. 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}).
  1. const userId = 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e';
  2. const productId = 'c28a2eaf-4d87-4f9f-ae5b-cbcf73e24253';
  3.  
  4. const user = await prisma.user.update({
  5. where: { id: userId },
  6. data: {
  7. savedProducts: {
  8. disconnect: {
  9. id: productId,
  10. },
  11. },
  12. },
  13. include: {
  14. savedProducts: true,
  15. },
  16. });
  17.  
  18. 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}).
  1. app.patch('/users/:id/saved-products', asyncHandler(async (req, res) => {
  2. assert(req.body, PostSavedProducts);
  3. const { id: userId } = req.params;
  4. const { productId } = req.body;
  5. const user = await prisma.user.update({
  6. where: { id: userId },
  7. data: {
  8. savedProducts: {
  9. connect: {
  10. id: productId,
  11. },
  12. },
  13. },
  14. include: {
  15. savedProducts: true,
  16. },
  17. });
  18. res.send(user);
  19. }));
  20.  
  21. app.patch('/users/:id/unsave-product', asyncHandler(async (req, res) => {
  22. assert(req.body, PostSavedProducts);
  23. const { id: userId } = req.params;
  24. const { productId } = req.body;
  25. const user = await prisma.user.update({
  26. where: { id: userId },
  27. data: {
  28. savedProducts: {
  29. disconnect: {
  30. id: productId,
  31. },
  32. },
  33. },
  34. include: {
  35. savedProducts: true,
  36. },
  37. });
  38. res.send(user);
  39. }));
▲ 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}).
  1. app.post('/orders', asyncHandler(async (req, res) => {
  2. assert(req.body, CreateOrder);
  3. const { userId, orderItems } = req.body;
  4.  
  5. const productIds = orderItems.map((orderItem) => orderItem.productId);
  6. const products = await prisma.product.findMany({
  7. where: { id: { in: productIds } },
  8. });
  9.  
  10. function getQuantity(productId) {
  11. const orderItem = orderItems.find(
  12. (orderItem) => orderItem.productId === productId
  13. );
  14. return orderItem.quantity;
  15. }
  16.  
  17. // 재고 확인
  18. const isSufficientStock = products.every((product) => {
  19. const { id, stock } = product;
  20. return stock >= getQuantity(id);
  21. });
  22.  
  23. if (!isSufficientStock) {
  24. throw new Error('Insufficient Stock');
  25. }
  26.  
  27. const [order] = await prisma.$transaction([
  28. prisma.order.create({
  29. data: {
  30. userId,
  31. orderItems: {
  32. create: orderItems,
  33. },
  34. },
  35. include: {
  36. orderItems: true,
  37. },
  38. }),
  39. ...orderItems.map(({ productId, quantity }) => {
  40. return prisma.product.update({
  41. where: { id: productId },
  42. data: {
  43. stock: {
  44. decrement: quantity,
  45. },
  46. },
  47. });
  48. })
  49. ]);
  50.  
  51. res.status(201).send(order);
  52. }));
▲ Hide
반응형
Get page views