{"id":4393,"date":"2024-12-14T12:00:50","date_gmt":"2024-12-14T05:00:50","guid":{"rendered":"https:\/\/www.marketenterprise.vn\/blog\/?p=4393"},"modified":"2024-12-16T08:19:11","modified_gmt":"2024-12-16T01:19:11","slug":"phan-1-rest-api-nestjs-va-prisma","status":"publish","type":"post","link":"https:\/\/www.marketenterprise.vn\/blog\/phan-1-rest-api-nestjs-va-prisma.html","title":{"rendered":"Ph\u1ea7n 1: H\u01b0\u1edbng d\u1eabn x\u00e2y d\u1ef1ng REST API v\u1edbi NestJS v\u00e0 Prisma m\u1ed9t c\u00e1ch CLEAR nh\u1ea5t."},"content":{"rendered":"\r\n<p>NestJS l\u00e0 m\u1ed9t trong nh\u1eefng framework n\u1ed5i b\u1eadt c\u1ee7a Node.js v\u00e0 g\u1ea7n \u0111\u00e2y \u0111\u00e3 nh\u1eadn \u0111\u01b0\u1ee3c r\u1ea5t nhi\u1ec1u s\u1ef1 y\u00eau th\u00edch v\u00e0 quan t\u00e2m t\u1eeb c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n. Trong qu\u00e1 tr\u00ecnh t\u00ecm ki\u1ebfm t\u00e0i li\u1ec7u m\u00ecnh c\u00f3 \u0111\u1ecdc \u0111\u01b0\u1ee3c m\u1ed9t chu\u1ed7i b\u00e0i vi\u1ebft kh\u00e1 hay v\u1ec1 c\u00e1ch x\u00e2y d\u1ef1ng m\u1ed9t \u1ee9ng d\u1ee5ng REST API backend v\u1edbi NestJS, <a href=\"https:\/\/www.marketenterprise.vn\/blog\/prisma-ho-tro-phat-trien-phan-1.html\">Prisma<\/a>, PostgreSQL v\u00e0 Swagger. M\u00ecnh xin ph\u00e9p \u0111\u01b0\u1ee3c chia s\u1ebb l\u1ea1i (Vi\u1ec7t h\u00f3a) theo nh\u1eefng g\u00ec m\u00ecnh hi\u1ec3u trong qu\u00e1 tr\u00ecnh th\u1ef1c h\u00e0nh \u0111\u1ec3 m\u1ecdi ng\u01b0\u1eddi d\u1ec5 theo d\u00f5i h\u01a1n.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\"><strong>N\u1ed9i dung ch\u00ednh<\/strong><\/h2>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Gi\u1edbi thi\u1ec7u<\/li>\r\n\r\n\r\n\r\n<li>Y\u00eau c\u1ea7u\r\n<ul class=\"wp-block-list\">\r\n<li>Ki\u1ebfn th\u1ee9c gi\u1ea3 \u0111\u1ecbnh<\/li>\r\n\r\n\r\n\r\n<li>M\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n<\/li>\r\n<\/ul>\r\n<\/li>\r\n\r\n\r\n\r\n<li>T\u1ea1o d\u1ef1 \u00e1n NestJS<\/li>\r\n\r\n\r\n\r\n<li>T\u1ea1o m\u1ed9t PostgreSQL Instance<\/li>\r\n\r\n\r\n\r\n<li>Thi\u1ebft l\u1eadp Prisma\r\n<ul class=\"wp-block-list\">\r\n<li>Kh\u1edfi t\u1ea1o Prisma<\/li>\r\n\r\n\r\n\r\n<li>C\u00e0i \u0111\u1eb7t bi\u1ebfn m\u00f4i tr\u01b0\u1eddng<\/li>\r\n\r\n\r\n\r\n<li>Hi\u1ec3u v\u1ec1 Prisma schema<\/li>\r\n\r\n\r\n\r\n<li>M\u00f4 h\u00ecnh h\u00f3a d\u1eef li\u1ec7u<\/li>\r\n\r\n\r\n\r\n<li>Migrate c\u01a1 s\u1edf d\u1eef li\u1ec7u<\/li>\r\n\r\n\r\n\r\n<li>Seed c\u01a1 s\u1edf d\u1eef li\u1ec7u<\/li>\r\n\r\n\r\n\r\n<li>T\u1ea1o Prisma service<\/li>\r\n<\/ul>\r\n<\/li>\r\n\r\n\r\n\r\n<li>Thi\u1ebft l\u1eadp Swagger<\/li>\r\n\r\n\r\n\r\n<li>Th\u1ef1c hi\u1ec7n c\u00e1c thao t\u00e1c CRUD cho <code>Product<\/code> model\r\n<ul class=\"wp-block-list\">\r\n<li>T\u1ea1o REST resources<\/li>\r\n\r\n\r\n\r\n<li>Th\u00eam PrismaClient v\u00e0o <code>Products<\/code> module<\/li>\r\n\r\n\r\n\r\n<li>\u0110\u1ecbnh ngh\u0129a endpoint <code>GET \/products<\/code><\/li>\r\n\r\n\r\n\r\n<li>\u0110\u1ecbnh ngh\u0129a endpoint <code>GET \/products\/drafts <\/code><\/li>\r\n\r\n\r\n\r\n<li>\u0110\u1ecbnh ngh\u0129a endpoint <code>GET \/products\/:id<\/code><\/li>\r\n\r\n\r\n\r\n<li>\u0110\u1ecbnh ngh\u0129a endpoint <code>POST\u00a0\/products<\/code><\/li>\r\n\r\n\r\n\r\n<li>\u0110\u1ecbnh ngh\u0129a endpoint <code>PATCH \/products\/:id<\/code><\/li>\r\n\r\n\r\n\r\n<li>\u0110\u1ecbnh ngh\u0129a endpoint <code>DELETE \/products\/:id<\/code><\/li>\r\n\r\n\r\n\r\n<li>Nh\u00f3m c\u00e1c endpoint trong Swagger<\/li>\r\n<\/ul>\r\n<\/li>\r\n\r\n\r\n\r\n<li>C\u1eadp nh\u1eadt response type trong Swagger<\/li>\r\n\r\n\r\n\r\n<li>T\u00f3m t\u1eaft v\u00e0 nh\u1eadn x\u00e9t<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Gi\u1edbi thi\u1ec7u<\/h2>\r\n\r\n\r\n\r\n<p><span>Trong h\u01b0\u1edbng d\u1eabn n\u00e0y, b\u1ea1n s\u1ebd h\u1ecdc c\u00e1ch x\u00e2y d\u1ef1ng backend REST API cho m\u1ed9t \u1ee9ng d\u1ee5ng qu\u1ea3n l\u00fd s\u1ea3n ph\u1ea9m <code>(Products-Management).<\/code> B\u1ea1n s\u1ebd b\u1eaft \u0111\u1ea7u b\u1eb1ng c\u00e1ch t\u1ea1o m\u1ed9t d\u1ef1 \u00e1n NestJS m\u1edbi, sau \u0111\u00f3 kh\u1edfi t\u1ea1o server PostgreSQL v\u00e0 k\u1ebft n\u1ed1i v\u1edbi n\u00f3 th\u00f4ng qua Prisma. Cu\u1ed1i c\u00f9ng, b\u1ea1n s\u1ebd x\u00e2y d\u1ef1ng REST API v\u00e0 t\u1ea1o t\u00e0i li\u1ec7u cho n\u00f3 b\u1eb1ng Swagger.<\/span><\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\"><span>C\u00e1c c\u00f4ng ngh\u1ec7 b\u1ea1n s\u1ebd s\u1eed d\u1ee5ng<\/span><\/h2>\r\n\r\n\r\n\r\n<p><span>Trong b\u00e0i vi\u1ebft n\u00e0y, ch\u00fang ta s\u1ebd s\u1eed d\u1ee5ng c\u00e1c c\u00f4ng ngh\u1ec7 sau:<\/span><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><span><strong>NestJS<\/strong><\/span><span>: Framework m\u1ea1nh m\u1ebd d\u1ef1a tr\u00ean Node.js, gi\u00fap ph\u00e1t tri\u1ec3n backend nhanh ch\u00f3ng v\u00e0 linh ho\u1ea1t.<\/span><\/li>\r\n\r\n\r\n\r\n<li><span><strong>Prisma<\/strong><\/span><span>: ORM (Object-Relational Mapping)\u00a0 \u0111\u1ec3 l\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u m\u1ed9t c\u00e1ch d\u1ec5 d\u00e0ng.<\/span><\/li>\r\n\r\n\r\n\r\n<li><span><strong>PostgreSQL<\/strong><\/span><span>: H\u1ec7 qu\u1ea3n tr\u1ecb c\u01a1 s\u1edf d\u1eef li\u1ec7u quan h\u1ec7 ph\u1ed5 bi\u1ebfn v\u00e0 m\u1ea1nh m\u1ebd.<\/span><\/li>\r\n\r\n\r\n\r\n<li><a href=\"https:\/\/www.marketenterprise.vn\/blog\/swagger-api-development-for-everyone.html\" target=\"_blank\" rel=\"noopener\"><span><strong>Swagger<\/strong><\/span><\/a><span>: C\u00f4ng c\u1ee5 t\u1ea1o API documents t\u1ef1 \u0111\u1ed9ng v\u00e0 h\u1ed7 tr\u1ee3 ki\u1ec3m th\u1eed tr\u1ef1c quan.<\/span><\/li>\r\n\r\n\r\n\r\n<li><a href=\"http:\/\/marketenterprise.vn\/blog\/tai-sao-nen-su-dung-typescript.html\" target=\"_blank\" rel=\"noopener\"><strong>Typescript<\/strong><\/a>: Ng\u00f4n ng\u1eef l\u1eadp tr\u00ecnh ta s\u1ebd s\u1eed d\u1ee5ng \u1edf b\u00e0i blog n\u00e0y.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Y\u00eau c\u1ea7u<\/h2>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Ki\u1ebfn th\u1ee9c gi\u1ea3 \u0111\u1ecbnh:<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t h\u01b0\u1edbng d\u1eabn ph\u00f9 h\u1ee3p v\u1edbi ng\u01b0\u1eddi m\u1edbi b\u1eaft \u0111\u1ea7u. Tuy nhi\u00ean, h\u01b0\u1edbng d\u1eabn n\u00e0y gi\u1ea3 \u0111\u1ecbnh r\u1eb1ng b\u1ea1n c\u00f3:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Ki\u1ebfn th\u1ee9c c\u01a1 b\u1ea3n v\u1ec1 JavaScript ho\u1eb7c TypeScript (\u01b0u ti\u00ean TypeScript)<\/li>\r\n\r\n\r\n\r\n<li>Ki\u1ebfn th\u1ee9c c\u01a1 b\u1ea3n v\u1ec1 NestJS<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd<\/strong>: N\u1ebfu b\u1ea1n ch\u01b0a quen v\u1edbi NestJS, b\u1ea1n c\u00f3 th\u1ec3 nhanh ch\u00f3ng t\u00ecm hi\u1ec3u nh\u1eefng ki\u1ebfn \u200b\u200bth\u1ee9c c\u01a1 b\u1ea3n b\u1eb1ng c\u00e1ch \u0111\u1ecdc qua ph\u1ea7n <a href=\"https:\/\/docs.nestjs.com\/first-steps\" target=\"_blank\" rel=\"noopener\">t\u1ed5ng quan<\/a> trong t\u00e0i li\u1ec7u NestJS.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">M\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u1ec3 l\u00e0m theo h\u01b0\u1edbng d\u1eabn n\u00e0y, b\u1ea1n c\u1ea7n:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>C\u00e0i \u0111\u1eb7t Node.js.<\/li>\r\n\r\n\r\n\r\n<li>C\u00e0i \u0111\u1eb7t Docker ho\u1eb7c PostgreSQL.<\/li>\r\n\r\n\r\n\r\n<li>C\u00e0i \u0111\u1eb7t Prisma VSCode Extension. (kh\u00f4ng b\u1eaft bu\u1ed9c)<\/li>\r\n\r\n\r\n\r\n<li>C\u00f3 quy\u1ec1n truy c\u1eadp v\u00e0o m\u1ed9t Unix shell (nh\u01b0 terminal\/shell trong Linux v\u00e0 macOS) \u0111\u1ec3 ch\u1ea1y c\u00e1c l\u1ec7nh trong lo\u1ea1t b\u00e0i n\u00e0y. (kh\u00f4ng b\u1eaft bu\u1ed9c)<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd 1<\/strong>: Prisma VSCode Extension kh\u00f4ng b\u1eaft bu\u1ed9c c\u00e0i \u0111\u1eb7t, nh\u01b0ng n\u00f3 cung c\u1ea5p IntelliSense h\u1ed7 tr\u1ee3 m\u00e0u s\u1eafc tr\u1ef1c quan, d\u1ec5 \u0111\u1ecdc.<\/em><\/p>\r\n\r\n\r\n\r\n<p><em><strong>L\u01b0u \u00fd 2:<\/strong> N\u1ebfu b\u1ea1n kh\u00f4ng c\u00f3 Unix shell (v\u00ed d\u1ee5: b\u1ea1n \u0111ang s\u1eed d\u1ee5ng m\u00e1y Windows), b\u1ea1n v\u1eabn c\u00f3 th\u1ec3 l\u00e0m theo h\u01b0\u1edbng d\u1eabn, nh\u01b0ng c\u00e1c l\u1ec7nh shell c\u00f3 th\u1ec3 c\u1ea7n \u0111\u01b0\u1ee3c ch\u1ec9nh s\u1eeda \u0111\u1ec3 ph\u00f9 h\u1ee3p v\u1edbi m\u00e1y c\u1ee7a b\u1ea1n.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id=\"generate-the-nestjs-project\">Kh\u1edfi t\u1ea1o d\u1ef1 \u00e1n NestJS<\/h2>\r\n\r\n\r\n\r\n<p>\u0110i\u1ec1u \u0111\u1ea7u ti\u00ean b\u1ea1n c\u1ea7n l\u00e0m l\u00e0 c\u00e0i \u0111\u1eb7t NestJS CLI. NestJS CLI r\u1ea5t ti\u1ec7n d\u1ee5ng khi l\u00e0m vi\u1ec7c v\u1edbi d\u1ef1 \u00e1n NestJS. N\u00f3 \u0111i k\u00e8m v\u1edbi c\u00e1c ti\u1ec7n \u00edch t\u00edch h\u1ee3p gi\u00fap b\u1ea1n kh\u1edfi t\u1ea1o, ph\u00e1t tri\u1ec3n v\u00e0 duy tr\u00ec \u1ee9ng d\u1ee5ng NestJS c\u1ee7a m\u00ecnh.<\/p>\r\n\r\n\r\n\r\n<p>B\u1ea1n c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng NestJS CLI \u0111\u1ec3 t\u1ea1o m\u1ed9t d\u1ef1 \u00e1n tr\u1ed1ng. \u0110\u1ec3 b\u1eaft \u0111\u1ea7u, h\u00e3y ch\u1ea1y l\u1ec7nh sau t\u1ea1i th\u01b0 m\u1ee5c m\u00e0 b\u1ea1n mu\u1ed1n d\u1ef1 \u00e1n s\u1ebd \u0111\u01b0\u1ee3c l\u01b0u tr\u1eef:<\/p>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>npx @nestjs\/cli new products-management<\/span><\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<div class=\"group\/conversation-turn relative flex w-full min-w-0 flex-col agent-turn\">\r\n<div class=\"flex-col gap-1 md:gap-3\">\r\n<div class=\"flex max-w-full flex-col flex-grow\">\r\n<div data-message-author-role=\"assistant\" data-message-id=\"897fd1be-7bf7-4131-95c8-daed93ebdb52\" dir=\"auto\" class=\"min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words [.text-message+&amp;]:mt-5\" data-message-model-slug=\"gpt-4o-canmore\">\r\n<div class=\"flex w-full flex-col gap-1 empty:hidden first:pt-[3px]\">\r\n<div class=\"markdown prose w-full break-words dark:prose-invert light\">\r\n<p>CLI s\u1ebd y\u00eau c\u1ea7u b\u1ea1n ch\u1ecdn tr\u00ecnh qu\u1ea3n l\u00fd package cho d\u1ef1 \u00e1n c\u1ee7a b\u1ea1n \u2014 h\u00e3y ch\u1ecdn <strong>npm<\/strong>. Sau \u0111\u00f3, d\u1ef1 \u00e1n NestJS s\u1ebd \u0111\u01b0\u1ee3c t\u1ef1 \u0111\u1ed9ng t\u1ea1o m\u1edbi trong th\u01b0 m\u1ee5c hi\u1ec7n t\u1ea1i.<\/p>\r\n<p>H\u00e3y m\u1edf d\u1ef1 \u00e1n b\u1eb1ng IDE b\u1ea5t k\u1ef3 (khuy\u1ebfn ngh\u1ecb s\u1eed d\u1ee5ng <a href=\"https:\/\/www.marketenterprise.vn\/blog\/gioi-thieu-ve-visual-studio-code-ky-i.html#Do_pho_bien_cua_VSCode\" target=\"_blank\" rel=\"noopener\">VSCode<\/a>). B\u1ea1n s\u1ebd th\u1ea5y c\u00e1c t\u1ec7p sau:<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">products-management\r\n  \u251c\u2500\u2500 node_modules\r\n  \u251c\u2500\u2500 src\r\n  \u2502   \u251c\u2500\u2500 app.controller.spec.ts\r\n  \u2502   \u251c\u2500\u2500 app.controller.ts\r\n  \u2502   \u251c\u2500\u2500 app.module.ts\r\n  \u2502   \u251c\u2500\u2500 app.service.ts\r\n  \u2502   \u2514\u2500\u2500 main.ts\r\n  \u251c\u2500\u2500 test\r\n  \u2502   \u251c\u2500\u2500 app.e2e-spec.ts\r\n  \u2502   \u2514\u2500\u2500 jest-e2e.json\r\n  \u251c\u2500\u2500 README.md\r\n  \u251c\u2500\u2500 nest-cli.json\r\n  \u251c\u2500\u2500 package-lock.json\r\n  \u251c\u2500\u2500 package.json\r\n  \u251c\u2500\u2500 tsconfig.build.json\r\n  \u2514\u2500\u2500 tsconfig.json<\/pre>\r\n<\/div>\r\n<\/div>\r\n<p>B\u1ea1n s\u1ebd thao t\u00e1c ch\u1ee7 y\u1ebfu \u1edf th\u01b0 m\u1ee5c <code>src<\/code>. NestJS CLI \u0111\u00e3 t\u1ea1o s\u1eb5n m\u1ed9t v\u00e0i t\u1eadp tin cho b\u1ea1n. M\u1ed9t s\u1ed1 t\u1eadp tin \u0111\u00e1ng ch\u00fa \u00fd l\u00e0:<\/p>\r\n<ul>\r\n<li><code>src\/app.module.ts<\/code>: Root Module c\u1ee7a \u1ee9ng d\u1ee5ng.<\/li>\r\n<li><code>src\/app.controller.ts<\/code>: M\u1ed9t <a href=\"https:\/\/www.marketenterprise.vn\/blog\/mo-hinh-mvc-trong-lap-trinh.html\" target=\"_blank\" rel=\"noopener\">controller<\/a> c\u01a1 b\u1ea3n v\u1edbi m\u1ed9t route: <code>\/<\/code>. Route n\u00e0y s\u1ebd tr\u1ea3 v\u1ec1 m\u1ed9t th\u00f4ng b\u00e1o \u0111\u01a1n gi\u1ea3n &#8216;Hello World!&#8217;.<\/li>\r\n<li><code>src\/main.ts<\/code>: Entry point c\u1ee7a \u1ee9ng d\u1ee5ng. N\u00f3 s\u1ebd kh\u1edfi \u0111\u1ed9ng \u1ee9ng d\u1ee5ng NestJS.<\/li>\r\n<\/ul>\r\n<p>B\u1ea1n c\u00f3 th\u1ec3 <code>start<\/code> d\u1ef1 \u00e1n c\u1ee7a m\u00ecnh b\u1eb1ng c\u00e1ch s\u1eed d\u1ee5ng l\u1ec7nh sau:<\/p>\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>npm run start:dev<\/span><\/code><\/pre>\r\n<\/div>\r\n<\/div>\r\n<p>L\u1ec7nh n\u00e0y s\u1ebd theo d\u00f5i c\u00e1c t\u1eadp tin c\u1ee7a b\u1ea1n, t\u1ef1 \u0111\u1ed9ng bi\u00ean d\u1ecbch v\u00e0 t\u1ea3i l\u1ea1i server m\u1ed7i khi b\u1ea1n th\u1ef1c hi\u1ec7n thay \u0111\u1ed5i. \u0110\u1ec3 \u0111\u1ea3m b\u1ea3o r\u1eb1ng server \u0111ang ch\u1ea1y, h\u00e3y truy c\u1eadp v\u00e0o URL <a href=\"http:\/\/localhost:3000\/\" target=\"_blank\" rel=\"noopener\">http:\/\/localhost:3000\/<\/a>. B\u1ea1n s\u1ebd th\u1ea5y m\u1ed9t trang m\u1eb7c \u0111\u1ecbnh v\u1edbi th\u00f4ng b\u00e1o <code>'Hello World!'<\/code>.<\/p>\r\n<blockquote>\r\n<p><em><strong>L\u01b0u \u00fd<\/strong>: B\u1ea1n n\u00ean gi\u1eef server ch\u1ea1y \u1edf ch\u1ebf \u0111\u1ed9 n\u1ec1n trong su\u1ed1t qu\u00e1 tr\u00ecnh th\u1ef1c hi\u1ec7n theo h\u01b0\u1edbng d\u1eabn n\u00e0y.<\/em><\/p>\r\n<\/blockquote>\r\n<h2 id=\"create-a-postgresql-instance\" class=\"sc-eDPFhE dpAVkV\">Kh\u1edfi t\u1ea1o PostgreSQL instance<\/h2>\r\n<p>Ch\u00fang ta s\u1ebd s\u1eed d\u1ee5ng PostgreSQL l\u00e0m c\u01a1 s\u1edf d\u1eef li\u1ec7u cho \u1ee9ng d\u1ee5ng NestJS n\u00e0y. H\u01b0\u1edbng d\u1eabn n\u00e0y s\u1ebd ch\u1ec9 cho b\u1ea1n c\u00e1ch c\u00e0i \u0111\u1eb7t v\u00e0 ch\u1ea1y PostgreSQL tr\u00ean m\u00e1y th\u00f4ng qua <a href=\"https:\/\/www.marketenterprise.vn\/blog\/docker-la-gi.html\" target=\"_blank\" rel=\"noopener\">Docker<\/a> container.<\/p>\r\n<blockquote>\r\n<p><em><strong>L\u01b0u \u00fd<\/strong>: N\u1ebfu b\u1ea1n kh\u00f4ng mu\u1ed1n s\u1eed d\u1ee5ng Docker, b\u1ea1n c\u00f3 th\u1ec3 c\u00e0i \u0111\u1eb7t PostgreSQL tr\u1ef1c ti\u1ebfp tr\u00ean m\u00e1y ho\u1eb7c s\u1eed d\u1ee5ng d\u1ecbch v\u1ee5 c\u01a1 s\u1edf d\u1eef li\u1ec7u PostgreSQL \u0111\u01b0\u1ee3c l\u01b0u tr\u1eef tr\u00ean Heroku.<\/em><\/p>\r\n<\/blockquote>\r\n<p>\u0110\u1ea7u ti\u00ean, h\u00e3y t\u1ea1o m\u1ed9t t\u1ec7p <code>docker-compose.yml<\/code> trong th\u01b0 m\u1ee5c ch\u00ednh c\u1ee7a d\u1ef1 \u00e1n:<\/p>\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>touch docker-compose.yml<\/span><\/code><\/pre>\r\n<\/div>\r\n<\/div>\r\n<p>T\u1ec7p <code>docker-compose.yml<\/code> l\u00e0 m\u1ed9t t\u1ec7p c\u1ea5u h\u00ecnh, ch\u1ee9a c\u00e1c th\u00f4ng s\u1ed1 \u0111\u1ec3 ch\u1ea1y m\u1ed9t Docker container v\u1edbi PostgreSQL \u0111\u01b0\u1ee3c c\u00e0i \u0111\u1eb7t b\u00ean trong. T\u1ea1o c\u1ea5u h\u00ecnh sau b\u00ean trong t\u1ec7p:<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\"># docker-compose.yml\r\n\r\nversion: '3.8'\r\nservices:\r\n\r\n  postgres:\r\n    image: postgres:13.5\r\n    restart: always\r\n    environment:\r\n      - POSTGRES_USER=myuser\r\n      - POSTGRES_PASSWORD=mypassword\r\n    volumes:\r\n      - postgres:\/var\/lib\/postgresql\/data\r\n    ports:\r\n      - '5432:5432'\r\n\r\nvolumes:\r\n  postgres:<\/pre>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><strong><code>image<\/code><\/strong>: X\u00e1c \u0111\u1ecbnh Docker image s\u1ebd s\u1eed d\u1ee5ng. \u1ede \u0111\u00e2y, b\u1ea1n \u0111ang s\u1eed d\u1ee5ng <code>postgres image<\/code> version 13.5.<\/li>\r\n\r\n\r\n\r\n<li><strong><code>environment<\/code><\/strong>: Ch\u1ec9 \u0111\u1ecbnh c\u00e1c bi\u1ebfn m\u00f4i tr\u01b0\u1eddng \u0111\u01b0\u1ee3c truy\u1ec1n v\u00e0o container trong qu\u00e1 tr\u00ecnh kh\u1edfi t\u1ea1o. B\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ecbnh ngh\u0129a c\u00e1c t\u00f9y ch\u1ecdn c\u1ea5u h\u00ecnh v\u00e0 th\u00f4ng tin b\u1ea3o m\u1eadt nh\u01b0 t\u00ean ng\u01b0\u1eddi d\u00f9ng v\u00e0 m\u1eadt kh\u1ea9u m\u00e0 container s\u1ebd s\u1eed d\u1ee5ng t\u1ea1i \u0111\u00e2y.<\/li>\r\n\r\n\r\n\r\n<li><strong><code>volumes<\/code><\/strong>: \u0110<span>\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 l\u01b0u tr\u1eef d\u1eef li\u1ec7u trong h\u1ec7 th\u1ed1ng.<\/span><\/li>\r\n\r\n\r\n\r\n<li><strong><code>ports<\/code><\/strong>: \u00c1nh x\u1ea1 c\u1ed5ng t\u1eeb m\u00e1y ch\u1ee7 sang container. \u0110\u1ecbnh d\u1ea1ng theo quy \u01b0\u1edbc <code>host_port:container_port<\/code>. Trong tr\u01b0\u1eddng h\u1ee3p n\u00e0y, b\u1ea1n \u0111ang \u00e1nh x\u1ea1 c\u1ed5ng <code>5432<\/code> c\u1ee7a m\u00e1y ch\u1ee7 sang c\u1ed5ng <code>5432<\/code> c\u1ee7a<code>postgres<\/code> container. <code>5432<\/code> th\u01b0\u1eddng l\u00e0 c\u1ed5ng \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng b\u1edfi PostgreSQL.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p>H\u00e3y \u0111\u1ea3m b\u1ea3o r\u1eb1ng kh\u00f4ng c\u00f3 g\u00ec \u0111ang ch\u1ea1y tr\u00ean c\u1ed5ng <code>5432<\/code> c\u1ee7a m\u00e1y b\u1ea1n. \u0110\u1ec3 kh\u1edfi \u0111\u1ed9ng <code>postgres<\/code> container, m\u1edf m\u1ed9t c\u1eeda s\u1ed5 terminal m\u1edbi v\u00e0 ch\u1ea1y l\u1ec7nh sau trong th\u01b0 m\u1ee5c ch\u00ednh c\u1ee7a d\u1ef1 \u00e1n:<\/p>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>docker-compose up<\/span><\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<p>N\u1ebfu m\u1ecdi th\u1ee9 ho\u1ea1t \u0111\u1ed9ng ch\u00ednh x\u00e1c, c\u1eeda s\u1ed5 terminal m\u1edbi s\u1ebd hi\u1ec3n th\u1ecb c\u00e1c log th\u00f4ng b\u00e1o r\u1eb1ng h\u1ec7 th\u1ed1ng c\u01a1 s\u1edf d\u1eef li\u1ec7u \u0111\u00e3 s\u1eb5n s\u00e0ng \u0111\u1ec3 ch\u1ea5p nh\u1eadn k\u1ebft n\u1ed1i.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">postgres-1  | 2024-12-04 03:29:37.464 UTC [1] LOG:  listening on IPv4 address \"0.0.0.0\", port 5432\r\npostgres-1  | 2024-12-04 03:29:37.464 UTC [1] LOG:  listening on IPv6 address \"::\", port 5432\r\npostgres-1  | 2024-12-04 03:29:37.467 UTC [1] LOG:  listening on Unix socket \"\/var\/run\/postgresql\/.s.PGSQL.5432\"\r\npostgres-1  | 2024-12-04 03:29:37.474 UTC [62] LOG:  database system was shut down at 2024-12-04 03:29:37 UTC\r\npostgres-1  | 2024-12-04 03:29:37.478 UTC [1] LOG:  database system is ready to accept connections<\/pre>\r\n\r\n\r\n\r\n<p>Ch\u00fac m\u1eebng b\u1ea1n \u0111\u00e3 t\u1ea1o th\u00e0nh c\u00f4ng, b\u00e2y gi\u1edd b\u1ea1n h\u00e3y chu\u1ea9n b\u1ecb m\u1ed9t t\u00e2m l\u00fd th\u1eadt tho\u1ea3i m\u00e1i \u0111\u1ec3 c\u00f3 th\u1ec3 kh\u00e1m ph\u00e1 nh\u1eefng \u0111i\u1ec1u r\u1ea5t th\u00fa v\u1ecb ti\u1ebfp theo.<\/p>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd:<\/strong> N\u1ebfu b\u1ea1n \u0111\u00f3ng c\u1eeda s\u1ed5 terminal, n\u00f3 c\u0169ng s\u1ebd d\u1eebng container. B\u1ea1n c\u00f3 th\u1ec3 tr\u00e1nh \u0111i\u1ec1u n\u00e0y b\u1eb1ng c\u00e1ch th\u00eam t\u00f9y ch\u1ecdn <code>-d<\/code> v\u00e0o cu\u1ed1i l\u1ec7nh, nh\u01b0 sau: <code>docker-compose up -d<\/code>. \u0110i\u1ec1u n\u00e0y s\u1ebd ch\u1ea1y container \u1edf ch\u1ebf \u0111\u1ed9 n\u1ec1n v\u00f4 th\u1eddi h\u1ea1n.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id=\"set-up-prisma\">Thi\u1ebft l\u1eadp Prisma<\/h2>\r\n\r\n\r\n\r\n<p><span>B\u00e2y gi\u1edd c\u01a1 s\u1edf d\u1eef li\u1ec7u \u0111\u00e3 s\u1eb5n s\u00e0ng, \u0111\u00e3 \u0111\u1ebfn l\u00fac thi\u1ebft l\u1eadp Prisma!<\/span><\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Kh\u1edfi t\u1ea1o Prisma<\/h3>\r\n\r\n\r\n\r\n<div class=\"gtx-body\">\u0110\u1ec3 b\u1eaft \u0111\u1ea7u, tr\u01b0\u1edbc ti\u00ean h\u00e3y c\u00e0i \u0111\u1eb7t Prisma CLI \u0111\u1ec3 th\u1ef1c hi\u1ec7n m\u1ed9t s\u1ed1 t\u00e1c v\u1ee5. Prisma CLI s\u1ebd cho ph\u00e9p b\u1ea1n ch\u1ea1y nhi\u1ec1u l\u1ec7nh kh\u00e1c nhau v\u00e0 t\u01b0\u01a1ng t\u00e1c v\u1edbi d\u1ef1 \u00e1n c\u1ee7a b\u1ea1n.<\/div>\r\n\r\n\r\n\r\n<div>\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>npm install -D prisma<\/span><\/code><\/pre>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<div><span>B\u1ea1n c\u00f3 th\u1ec3 kh\u1edfi t\u1ea1o Prisma b\u00ean trong d\u1ef1 \u00e1n c\u1ee7a m\u00ecnh b\u1eb1ng c\u00e1ch ch\u1ea1y l\u1ec7nh sau:<\/span><\/div>\r\n\r\n\r\n\r\n<div>\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>npx prisma init<\/span><\/code><\/pre>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<div>L\u1ec7nh n\u00e0y s\u1ebd t\u1ea1o m\u1ed9t th\u01b0 m\u1ee5c <code>prisma<\/code> m\u1edbi v\u1edbi t\u1ec7p <code>schema.prisma<\/code>. \u0110\u00e2y l\u00e0 t\u1ec7p c\u1ea5u h\u00ecnh ch\u00ednh ch\u1ee9a schema c\u1ee7a c\u01a1 s\u1edf d\u1eef li\u1ec7u. L\u1ec7nh n\u00e0y c\u0169ng t\u1ea1o m\u1ed9t t\u1ec7p <code>.env<\/code> b\u00ean trong d\u1ef1 \u00e1n c\u1ee7a b\u1ea1n.<\/div>\r\n\r\n\r\n\r\n<div>\r\n<h3 id=\"set-your-environment-variable\" class=\"sc-dLNtp SXCGs\">C\u00e0i \u0111\u1eb7t bi\u1ebfn m\u00f4i tr\u01b0\u1eddng<\/h3>\r\n<\/div>\r\n\r\n\r\n\r\n<div>\r\n<div class=\"group\/conversation-turn relative flex w-full min-w-0 flex-col agent-turn\">\r\n<div class=\"flex-col gap-1 md:gap-3\">\r\n<div class=\"flex max-w-full flex-col flex-grow\">\r\n<div data-message-author-role=\"assistant\" data-message-id=\"b19a6f12-009a-4179-a5d3-0b6166addd5a\" dir=\"auto\" class=\"min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words [.text-message+&amp;]:mt-5\" data-message-model-slug=\"gpt-4o-canmore\">\r\n<div class=\"flex w-full flex-col gap-1 empty:hidden first:pt-[3px]\">\r\n<div class=\"markdown prose w-full break-words dark:prose-invert light\">\r\n<p>B\u00ean trong t\u1ec7p <code>.env<\/code>, b\u1ea1n s\u1ebd th\u1ea5y m\u1ed9t bi\u1ebfn m\u00f4i tr\u01b0\u1eddng <code>DATABASE_URL<\/code> v\u1edbi m\u1ed9t <code>connection string<\/code> (chu\u1ed7i k\u1ebft n\u1ed1i) theo m\u1ed9t template. H\u00e3y thay th\u1ebf chu\u1ed7i k\u1ebft n\u1ed1i n\u00e0y b\u1eb1ng chu\u1ed7i k\u1ebft n\u1ed1i v\u00f3i PostgreSQL c\u1ee7a b\u1ea1n.<\/p>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<div>\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>\/\/.env\r\nDATABASE_URL=\"postgres:\/\/myuser:mypassword@localhost:5432\/postgres\"<\/span><\/code><\/pre>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd:<\/strong> N\u1ebfu b\u1ea1n kh\u00f4ng s\u1eed d\u1ee5ng Docker (nh\u01b0 \u0111\u00e3 ch\u1ec9 ra trong ph\u1ea7n tr\u01b0\u1edbc) \u0111\u1ec3 t\u1ea1o c\u01a1 s\u1edf d\u1eef li\u1ec7u PostgreSQL, chu\u1ed7i k\u1ebft n\u1ed1i c\u1ee7a b\u1ea1n s\u1ebd kh\u00e1c v\u1edbi chu\u1ed7i \u0111\u01b0\u1ee3c hi\u1ec3n th\u1ecb \u1edf tr\u00ean. \u0110\u1ecbnh d\u1ea1ng chu\u1ed7i k\u1ebft n\u1ed1i cho PostgreSQL c\u00f3 s\u1eb5n trong t\u00e0i li\u1ec7u <a href=\"https:\/\/www.prisma.io\/docs\/orm\/overview\/databases\/postgresql#connection-url\" target=\"_blank\" rel=\"noopener\">Prisma<\/a>.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"understand-the-prisma-schema\">Gi\u1ea3i th\u00edch m\u1ed9t ch\u00fat v\u1ec1 Prisma schema<\/h3>\r\n\r\n\r\n\r\n<p><span>N\u1ebfu b\u1ea1n m\u1edf <code>prisma\/schema.prisma<\/code>, b\u1ea1n s\u1ebd th\u1ea5y\u00a0 schema m\u1eb7c \u0111\u1ecbnh sau:<\/span><\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ prisma\/schema.prisma\r\n\r\ngenerator client {\r\n  provider = \"prisma-client-js\"\r\n}\r\n\r\ndatasource db {\r\n  provider = \"postgresql\"\r\n  url      = env(\"DATABASE_URL\")\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>T\u1ec7p n\u00e0y \u0111\u01b0\u1ee3c vi\u1ebft b\u1eb1ng <strong>Prisma Schema Language<\/strong>, l\u00e0 m\u1ed9t ng\u00f4n ng\u1eef m\u00e0 Prisma s\u1eed d\u1ee5ng \u0111\u1ec3 \u0111\u1ecbnh ngh\u0129a schema cho c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n. T\u1ec7p <code>schema.prisma<\/code> c\u00f3 ba th\u00e0nh ph\u1ea7n ch\u00ednh:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><strong>Data source<\/strong>: Ch\u1ec9 \u0111\u1ecbnh k\u1ebft n\u1ed1i \u0111\u1ebfn c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n. C\u1ea5u h\u00ecnh \u1edf tr\u00ean c\u00f3 ngh\u0129a <code>provider<\/code> l\u00e0 PostgreSQL v\u00e0 chu\u1ed7i k\u1ebft n\u1ed1i c\u01a1 s\u1edf d\u1eef li\u1ec7u \u0111\u01b0\u1ee3c l\u01b0u trong bi\u1ebfn m\u00f4i tr\u01b0\u1eddng <code>DATABASE_URL<\/code>.<\/li>\r\n\r\n\r\n\r\n<li><strong>Generator<\/strong>: Ch\u1ec9 \u0111\u1ecbnh r\u1eb1ng b\u1ea1n mu\u1ed1n t\u1ea1o <strong>Prisma Client<\/strong>, m\u1ed9t tr\u00ecnh x\u00e2y d\u1ef1ng truy v\u1ea5n <code>type-safe<\/code> cho c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n. N\u00f3 \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 g\u1eedi c\u00e1c truy v\u1ea5n \u0111\u1ebfn c\u01a1 s\u1edf d\u1eef li\u1ec7u.<\/li>\r\n\r\n\r\n\r\n<li><strong>Data model<\/strong>: \u0110\u1ecbnh ngh\u0129a c\u00e1c models \u0111\u1ec3 t\u1ea1o database c\u1ee7a b\u1ea1n. M\u1ed7i model s\u1ebd \u0111\u01b0\u1ee3c \u00e1nh x\u1ea1 th\u00e0nh m\u1ed9t b\u1ea3ng trong c\u01a1 s\u1edf d\u1eef li\u1ec7u. Hi\u1ec7n t\u1ea1i ch\u01b0a c\u00f3 model n\u00e0o trong schema c\u1ee7a b\u1ea1n, ph\u1ea7n n\u00e0y s\u1ebd \u0111\u01b0\u1ee3c t\u00ecm hi\u1ec3u trong ph\u1ea7n ti\u1ebfp theo.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd<\/strong>: \u0110\u1ec3 bi\u1ebft th\u00eam th\u00f4ng tin v\u1ec1 schema c\u1ee7a Prisma, h\u00e3y xem <a href=\"https:\/\/www.prisma.io\/docs\/orm\/prisma-schema\" target=\"_blank\" rel=\"noopener\">Prisma Docs<\/a>.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"model-the-data\">M\u00f4 h\u00ecnh h\u00f3a d\u1eef li\u1ec7u<\/h3>\r\n\r\n\r\n\r\n<p>B\u00e2y gi\u1edd l\u00e0 l\u00fac \u0111\u1ecbnh ngh\u0129a c\u00e1c models d\u1eef li\u1ec7u cho \u1ee9ng d\u1ee5ng c\u1ee7a b\u1ea1n. Trong h\u01b0\u1edbng d\u1eabn n\u00e0y, b\u1ea1n ch\u1ec9 c\u1ea7n m\u1ed9t model <code>Product<\/code> \u0111\u1ec3 \u0111\u1ea1i di\u1ec7n cho m\u1ed7i s\u1ea3n ph\u1ea9m c\u1ee7a b\u1ea1n.<\/p>\r\n\r\n\r\n\r\n<p>B\u00ean trong t\u1ec7p <code>prisma\/schema.prisma<\/code>, h\u00e3y th\u00eam m\u1ed9t model m\u1edbi v\u00e0o schema c\u1ee7a b\u1ea1n c\u00f3 t\u00ean l\u00e0 <code>Product<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">model Product {\r\n  id          Int      @id @default(autoincrement())\r\n  name        String   @unique\r\n  description String?\r\n  published   Boolean  @default(false)\r\n  createdAt   DateTime @default(now())\r\n  updatedAt   DateTime @updatedAt\r\n}\r\n<\/pre>\r\n\r\n\r\n\r\n<p>\u1ede \u0111\u00e2y, b\u1ea1n \u0111\u00e3 t\u1ea1o m\u1ed9t model <code>Product<\/code> v\u1edbi m\u1ed9t s\u1ed1 tr\u01b0\u1eddng. M\u1ed7i tr\u01b0\u1eddng c\u00f3 m\u1ed9t t\u00ean (id, name, v.v.), m\u1ed9t ki\u1ec3u d\u1eef li\u1ec7u (Int, String, v.v.), v\u00e0 c\u00e1c thu\u1ed9c t\u00ednh t\u00f9y ch\u1ecdn kh\u00e1c (nh\u01b0 <code>@id<\/code>, <code>@unique<\/code>, v.v.). C\u00e1c tr\u01b0\u1eddng optional c\u00f3 th\u1ec3\u00a0 \u0111\u1ecbnh ngh\u0129a b\u1eb1ng c\u00e1ch th\u00eam d\u1ea5u <code>?<\/code> sau ki\u1ec3u d\u1eef li\u1ec7u c\u1ee7a tr\u01b0\u1eddng \u0111\u00f3.<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Tr\u01b0\u1eddng <code>id<\/code> c\u00f3 m\u1ed9t thu\u1ed9c t\u00ednh \u0111\u1eb7c bi\u1ec7t l\u00e0 <code>@id<\/code>. Thu\u1ed9c t\u00ednh n\u00e0y ch\u1ec9 ra r\u1eb1ng tr\u01b0\u1eddng n\u00e0y l\u00e0 kh\u00f3a ch\u00ednh c\u1ee7a model. Thu\u1ed9c t\u00ednh <code>@default(autoincrement())<\/code> ch\u1ec9 ra r\u1eb1ng tr\u01b0\u1eddng n\u00e0y s\u1ebd \u0111\u01b0\u1ee3c t\u1ef1 \u0111\u1ed9ng t\u0103ng v\u00e0 g\u00e1n cho m\u1ed7i b\u1ea3n ghi m\u1edbi \u0111\u01b0\u1ee3c t\u1ea1o.<\/li>\r\n\r\n\r\n\r\n<li>Tr\u01b0\u1eddng <code>published<\/code> l\u00e0 m\u1ed9t c\u1edd \u0111\u1ec3 ch\u1ec9 ra s\u1ea3n ph\u1ea9m \u0111\u00e3 \u0111\u01b0\u1ee3c c\u00f4ng khai hay \u1ea9n \u0111i. Thu\u1ed9c t\u00ednh <code>@default(false)<\/code> ch\u1ec9 ra r\u1eb1ng tr\u01b0\u1eddng n\u00e0y s\u1ebd \u0111\u01b0\u1ee3c \u0111\u1eb7t gi\u00e1 tr\u1ecb <code>false<\/code> theo m\u1eb7c \u0111\u1ecbnh.<\/li>\r\n\r\n\r\n\r\n<li>Hai tr\u01b0\u1eddng <code>DateTime<\/code> l\u00e0 <code>createdAt<\/code> v\u00e0 <code>updatedAt<\/code> s\u1ebd theo d\u00f5i th\u1eddi \u0111i\u1ec3m t\u1ea1o v\u00e0 th\u1eddi \u0111i\u1ec3m cu\u1ed1i c\u00f9ng s\u1ea3n ph\u1ea9m \u0111\u01b0\u1ee3c c\u1eadp nh\u1eadt. Thu\u1ed9c t\u00ednh <code>@updatedAt<\/code> s\u1ebd t\u1ef1 \u0111\u1ed9ng c\u1eadp nh\u1eadt tr\u01b0\u1eddng n\u00e0y v\u1edbi th\u1eddi gian hi\u1ec7n t\u1ea1i m\u1ed7i khi s\u1ea3n ph\u1ea9m \u0111\u01b0\u1ee3c s\u1eeda \u0111\u1ed5i.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"migrate-the-database\">Migrate c\u01a1 s\u1edf d\u1eef li\u1ec7u<\/h3>\r\n\r\n\r\n\r\n<p><span>V\u1edbi Prisma schema v\u1eeba \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a, b\u1ea1n s\u1ebd ph\u1ea3i th\u1ef1c hi\u1ec7n migrations \u0111\u1ec3 t\u1ea1o c\u00e1c b\u1ea3ng th\u1ef1c t\u1ebf trong c\u01a1 s\u1edf d\u1eef li\u1ec7u. \u0110\u1ec3 t\u1ea1o v\u00e0 th\u1ef1c thi migration l\u1ea7n \u0111\u1ea7u ti\u00ean, h\u00e3y ch\u1ea1y l\u1ec7nh sau trong terminal:<\/span><\/p>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>npx prisma migrate dev --name \"init\"<\/span><\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<p>L\u1ec7nh n\u00e0y s\u1ebd th\u1ef1c hi\u1ec7n ba vi\u1ec7c:<\/p>\r\n\r\n\r\n\r\n<ol class=\"wp-block-list\">\r\n<li><strong>L\u01b0u migration<\/strong>: <strong>Prisma Migrate<\/strong> s\u1ebd ghi l\u1ea1i m\u1ed9t b\u1ea3n sao \u0111\u1ed1i v\u1edbi schema c\u1ee7a b\u1ea1n v\u00e0 x\u00e1c \u0111\u1ecbnh c\u00e1c l\u1ec7nh SQL c\u1ea7n thi\u1ebft \u0111\u1ec3 th\u1ef1c hi\u1ec7n migration. Prisma s\u1ebd l\u01b0u t\u1ec7p migration ch\u1ee9a c\u00e1c l\u1ec7nh SQL v\u00e0o th\u01b0 m\u1ee5c <code>prisma\/migrations<\/code> m\u1edbi \u0111\u01b0\u1ee3c t\u1ea1o.<\/li>\r\n\r\n\r\n\r\n<li><strong>Th\u1ef1c thi migration<\/strong>: <strong>Prisma Migrate<\/strong> s\u1ebd th\u1ef1c thi c\u00e1c l\u1ec7nh SQL trong t\u1ec7p migration \u0111\u1ec3 t\u1ea1o c\u00e1c b\u1ea3ng trong c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n.<\/li>\r\n\r\n\r\n\r\n<li><strong>T\u1ea1o Prisma Client<\/strong>: <strong>Prisma<\/strong> s\u1ebd t\u1ea1o <strong>Prisma Client<\/strong> d\u1ef1a tr\u00ean schema m\u1edbi nh\u1ea5t c\u1ee7a b\u1ea1n. V\u00ec b\u1ea1n ch\u01b0a c\u00e0i \u0111\u1eb7t th\u01b0 vi\u1ec7n Client, CLI c\u0169ng s\u1ebd t\u1ef1 \u0111\u1ed9ng c\u00e0i \u0111\u1eb7t n\u00f3 cho b\u1ea1n. B\u1ea1n s\u1ebd th\u1ea5y g\u00f3i <strong>@prisma\/client<\/strong> trong ph\u1ea7n <code>dependencies<\/code> c\u1ee7a t\u1ec7p <code>package.json<\/code>. <strong>Prisma Client<\/strong> l\u00e0 m\u1ed9t <span><em>TypeScript query builder<\/em> <\/span>\u00a0\u0111\u01b0\u1ee3c t\u1ea1o t\u1ef1 \u0111\u1ed9ng t\u1eeb schema c\u1ee7a Prisma. N\u00f3 \u0111\u01b0\u1ee3c \u0111i\u1ec1u ch\u1ec9nh ph\u00f9 h\u1ee3p v\u1edbi schema c\u1ee7a b\u1ea1n v\u00e0 s\u1ebd \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 g\u1eedi truy v\u1ea5n \u0111\u1ebfn c\u01a1 s\u1edf d\u1eef li\u1ec7u.<\/li>\r\n<\/ol>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd<\/strong>: B\u1ea1n c\u00f3 th\u1ec3 t\u00ecm hi\u1ec3u th\u00eam v\u1ec1 <a href=\"https:\/\/www.prisma.io\/docs\/orm\/prisma-migrate\" target=\"_blank\" rel=\"noopener\">Prisma Migrate<\/a> trong t\u00e0i li\u1ec7u Prisma.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<p>N\u1ebfu ch\u1ea1y l\u1ec7nh th\u00e0nh c\u00f4ng, b\u1ea1n s\u1ebd th\u1ea5y th\u00f4ng b\u00e1o nh\u01b0 sau:<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"635\" height=\"244\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04112035\/Screenshot-2024-12-04-at-11.19.56.png\" alt=\"K\u1ebft qu\u1ea3 mirate database\" class=\"wp-image-4420\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04112035\/Screenshot-2024-12-04-at-11.19.56.png 635w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04112035\/Screenshot-2024-12-04-at-11.19.56-300x115.png 300w\" sizes=\"auto, (max-width: 635px) 100vw, 635px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<p>Ki\u1ec3m tra t\u1ec7p migration \u0111\u01b0\u1ee3c t\u1ea1o \u0111\u1ec3 hi\u1ec3u r\u00f5 h\u01a1n v\u1ec1 nh\u1eefng g\u00ec <strong>Prisma Migrate<\/strong> \u0111\u00e3 th\u1ef1c hi\u1ec7n trong d\u1ef1 \u00e1n c\u1ee7a b\u1ea1n:<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"669\" height=\"291\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04113208\/Screenshot-2024-12-04-at-11.31.49.png\" alt=\"N\u1ed9i dung file migrations\" class=\"wp-image-4422\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04113208\/Screenshot-2024-12-04-at-11.31.49.png 669w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04113208\/Screenshot-2024-12-04-at-11.31.49-300x130.png 300w\" sizes=\"auto, (max-width: 669px) 100vw, 669px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd:<\/strong> T\u00ean t\u1ec7p schema c\u1ee7a b\u1ea1n s\u1ebd h\u01a1i kh\u00e1c m\u1ed9t ch\u00fat.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<p><span>\u0110\u00e2y l\u00e0 SQL c\u1ea7n thi\u1ebft \u0111\u1ec3 t\u1ea1o table <code>Product<\/code> b\u00ean trong c\u01a1 s\u1edf d\u1eef li\u1ec7u PostgreSQL c\u1ee7a b\u1ea1n. N\u00f3 \u0111\u01b0\u1ee3c Prisma t\u1ef1 \u0111\u1ed9ng t\u1ea1o v\u00e0 th\u1ef1c thi d\u1ef1a tr\u00ean Prisma schema.<\/span><\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"seed-the-database\">Seed c\u01a1 s\u1edf d\u1eef li\u1ec7u<\/h3>\r\n\r\n\r\n\r\n<p>Hi\u1ec7n t\u1ea1i, c\u01a1 s\u1edf d\u1eef li\u1ec7u \u0111ang tr\u1ed1ng. V\u00ec v\u1eady, b\u1ea1n s\u1ebd t\u1ea1o m\u1ed9t <code>seed script<\/code> \u0111\u1ec3 \u0111i\u1ec1n m\u1ed9t s\u1ed1 d\u1eef li\u1ec7u m\u1eabu v\u00e0o c\u01a1 s\u1edf d\u1eef li\u1ec7u.<\/p>\r\n\r\n\r\n\r\n<p>\u0110\u1ea7u ti\u00ean, h\u00e3y t\u1ea1o m\u1ed9t t\u1ec7p seed c\u00f3 t\u00ean l\u00e0 <code>prisma\/seed.ts<\/code>. T\u1ec7p n\u00e0y s\u1ebd ch\u1ee9a d\u1eef li\u1ec7u m\u1eabu v\u00e0 c\u00e1c truy v\u1ea5n c\u1ea7n thi\u1ebft \u0111\u1ec3 \u0111i\u1ec1n v\u00e0o c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n.<\/p>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>touch prisma\/seed.ts<\/span><\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<p><span>Sau \u0111\u00f3, b\u00ean trong t\u1ec7p <code>seed.ts<\/code>, th\u00eam \u0111o\u1ea1n m\u00e3 sau:<\/span><\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ prisma\/seed.ts\r\n\r\nimport { PrismaClient } from '@prisma\/client';\r\n\r\n\/\/ initialize Prisma Client\r\nconst prisma = new PrismaClient();\r\n\r\nasync function main() {\r\n  const product1 = await prisma.product.upsert({\r\n    where: { name: 'MEVN Prodcut 1' },\r\n    update: {},\r\n    create: {\r\n      name: 'MEVN Product1',\r\n      description:\r\n        'This is MEVN Product1. This is a description of MEVN Product1.',\r\n      published: false,\r\n    },\r\n  });\r\n  const product2 = await prisma.product.upsert({\r\n    where: { name: 'MEVN Product2' },\r\n    update: {},\r\n    create: {\r\n      name: 'MEVN Product2',\r\n      description:\r\n        'This is MEVN Product2. This is a description of MEVN Product2.',\r\n      published: false,\r\n    },\r\n  });\r\n\r\n  console.log({ product1, product2 });\r\n}\r\n\r\n\/\/ execute the main function\r\nmain()\r\n  .catch((e) =&gt; {\r\n    console.error(e);\r\n    process.exit(1);\r\n  })\r\n  .finally(async () =&gt; {\r\n    \/\/ close Prisma Client at the end\r\n    await prisma.$disconnect();\r\n  });\r\n<\/pre>\r\n\r\n\r\n\r\n<p>B\u00ean trong script n\u00e0y, b\u1ea1n s\u1ebd kh\u1edfi t\u1ea1o <strong>Prisma Client<\/strong> tr\u01b0\u1edbc. Sau \u0111\u00f3, b\u1ea1n t\u1ea1o hai s\u1ea3n ph\u1ea9m c\u00e1ch s\u1eed d\u1ee5ng h\u00e0m <code>prisma.upsert()<\/code>. H\u00e0m <code>upsert<\/code> ch\u1ec9 t\u1ea1o s\u1ea3n ph\u1ea9m m\u1edbi n\u1ebfu kh\u00f4ng c\u00f3 s\u1ea3n ph\u1ea9m n\u00e0o ph\u00f9 h\u1ee3p v\u1edbi \u0111i\u1ec1u ki\u1ec7n <code>where<\/code>. B\u1ea1n s\u1eed d\u1ee5ng truy v\u1ea5n <strong>upsert<\/strong> thay v\u00ec truy v\u1ea5n <strong>create<\/strong> v\u00ec <strong>upsert<\/strong> gi\u00fap tr\u00e1nh l\u1ed7i li\u00ean quan \u0111\u1ebfn vi\u1ec7c v\u00f4 t\u00ecnh c\u1ed1 g\u1eafng ch\u00e8n c\u00f9ng m\u1ed9t b\u1ea3n ghi hai l\u1ea7n.<\/p>\r\n\r\n\r\n\r\n<p>B\u1ea1n c\u1ea7n ch\u1ec9 \u0111\u1ecbnh cho Prisma bi\u1ebft script n\u00e0o s\u1ebd \u0111\u01b0\u1ee3c th\u1ef1c thi khi ch\u1ea1y l\u1ec7nh seeding. B\u1ea1n c\u00f3 th\u1ec3 l\u00e0m \u0111i\u1ec1u n\u00e0y b\u1eb1ng c\u00e1ch th\u00eam key <code>prisma.seed<\/code> v\u00e0o cu\u1ed1i t\u1ec7p <code>package.json<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\" data-enlighter-theme=\"\" data-enlighter-highlight=\"16-18\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ package.json\r\n\r\n\/\/ ...\r\n  \"scripts\": {\r\n    \/\/ ...\r\n  },\r\n  \"dependencies\": {\r\n    \/\/ ...\r\n  },\r\n  \"devDependencies\": {\r\n    \/\/ ...\r\n  },\r\n  \"jest\": {\r\n    \/\/ ...\r\n  },\r\n  \"prisma\": {\r\n    \"seed\": \"ts-node prisma\/seed.ts\"\r\n  }<\/pre>\r\n\r\n\r\n\r\n<p>L\u1ec7nh <strong>seed<\/strong> s\u1ebd th\u1ef1c thi script <code>prisma\/seed.ts<\/code> m\u00e0 b\u1ea1n \u0111\u00e3 \u0111\u1ecbnh ngh\u0129a tr\u01b0\u1edbc \u0111\u00f3. L\u1ec7nh n\u00e0y s\u1ebd ho\u1ea1t \u0111\u1ed9ng t\u1ef1 \u0111\u1ed9ng v\u00ec <strong>ts-node<\/strong> \u0111\u00e3 \u0111\u01b0\u1ee3c c\u00e0i \u0111\u1eb7t d\u01b0\u1edbi d\u1ea1ng <strong>dev dependency<\/strong> trong t\u1ec7p <code>package.json<\/code> c\u1ee7a b\u1ea1n.<\/p>\r\n\r\n\r\n\r\n<p>Th\u1ef1c hi\u1ec7n seeding b\u1eb1ng l\u1ec7nh sau:<\/p>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code>npx prisma db seed\r\n<\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<p>L\u1ec7nh n\u00e0y s\u1ebd ch\u1ea1y script seed \u0111\u1ec3 \u0111i\u1ec1n d\u1eef li\u1ec7u m\u1eabu v\u00e0o c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n. Sau khi th\u1ef1c thi xong, b\u1ea1n s\u1ebd th\u1ea5y th\u00f4ng b\u00e1o nh\u01b0 sau:<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"620\" height=\"314\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171055\/Screenshot-2024-12-05-at-17.10.15.png\" alt=\"Seed data v\u00e0o PostreSQL v\u1edbi Prisma\" class=\"wp-image-4450\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171055\/Screenshot-2024-12-05-at-17.10.15.png 620w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171055\/Screenshot-2024-12-05-at-17.10.15-300x152.png 300w\" sizes=\"auto, (max-width: 620px) 100vw, 620px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd:<\/strong> B\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ecdc th\u00eam v\u1ec1 seeding \u1edf <a href=\"https:\/\/www.prisma.io\/docs\/orm\/prisma-migrate\/workflows\/seeding\" target=\"_blank\" rel=\"noopener\">Prisma Docs<\/a><\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"create-a-prisma-service\">T\u1ea1o Prisma service<\/h3>\r\n\r\n\r\n\r\n<div class=\"group\/conversation-turn relative flex w-full min-w-0 flex-col agent-turn\">\r\n<div class=\"flex-col gap-1 md:gap-3\">\r\n<div class=\"flex max-w-full flex-col flex-grow\">\r\n<div data-message-author-role=\"assistant\" data-message-id=\"79c8eded-bbce-463c-a469-f7bf0b166189\" dir=\"auto\" class=\"min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words [.text-message+&amp;]:mt-5\" data-message-model-slug=\"gpt-4o-canmore\">\r\n<div class=\"flex w-full flex-col gap-1 empty:hidden first:pt-[3px]\">\r\n<div class=\"markdown prose w-full break-words dark:prose-invert light\">\r\n<p>Trong \u1ee9ng d\u1ee5ng <strong>NestJS, <\/strong> b\u1ea1n n\u00ean t\u00e1ch <code>Prisma Client API<\/code> ra kh\u1ecfi \u1ee9ng d\u1ee5ng. \u0110\u1ec3 l\u00e0m \u0111i\u1ec1u n\u00e0y, b\u1ea1n s\u1ebd t\u1ea1o m\u1ed9t <strong>service<\/strong> m\u1edbi \u0111\u1ec3 ch\u1ee9a <strong>Prisma Client<\/strong>. Service n\u00e0y, g\u1ecdi l\u00e0 <code>PrismaService<\/code>, s\u1ebd ch\u1ecbu tr\u00e1ch nhi\u1ec7m kh\u1edfi t\u1ea1o m\u1ed9t instance c\u1ee7a <strong>PrismaClient<\/strong> v\u00e0 k\u1ebft n\u1ed1i v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n.<\/p>\r\n<p><strong>Nest CLI<\/strong> cung c\u1ea5p c\u00e1ch d\u1ec5 d\u00e0ng \u0111\u1ec3 t\u1ea1o <strong>module<\/strong> v\u00e0 <strong>service<\/strong> tr\u1ef1c ti\u1ebfp t\u1eeb d\u00f2ng l\u1ec7nh (CLI). Ch\u1ea1y l\u1ec7nh sau trong terminal c\u1ee7a b\u1ea1n:<\/p>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code><span>npx nest generate module prisma<\/span>\r\n\r\n<span>npx nest generate service prisma<\/span><\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd 1:<\/strong> N\u1ebfu c\u1ea7n, h\u00e3y tham kh\u1ea3o t\u00e0i li\u1ec7u c\u1ee7a <strong>NestJS<\/strong> \u0111\u1ec3 c\u00f3 c\u00e1i nh\u00ecn t\u1ed5ng quan v\u1ec1 <a href=\"https:\/\/docs.nestjs.com\/providers\" target=\"_blank\" rel=\"noopener\">services<\/a> v\u00e0 <a href=\"https:\/\/docs.nestjs.com\/modules\" target=\"_blank\" rel=\"noopener\">modules<\/a>.<\/em><\/p>\r\n\r\n\r\n\r\n<p><em><strong>L\u01b0u \u00fd 2:<\/strong> Trong m\u1ed9t s\u1ed1 tr\u01b0\u1eddng h\u1ee3p, khi ch\u1ea1y l\u1ec7nh <strong>nest generate<\/strong> trong khi server \u0111ang ch\u1ea1y, NestJS c\u00f3 th\u1ec3 throw exception v\u1edbi th\u00f4ng b\u00e1o: <code>Error: Cannot find module '.\/app.controller'<\/code>. N\u1ebfu g\u1eb7p ph\u1ea3i l\u1ed7i n\u00e0y, h\u00e3y ch\u1ea1y l\u1ec7nh sau t\u1eeb terminal. Sau \u0111\u00f3, kh\u1edfi \u0111\u1ed9ng l\u1ea1i server c\u1ee7a b\u1ea1n.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code>rm -rf dist\r\n<\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<p>L\u1ec7nh n\u00e0y s\u1ebd t\u1ea1o m\u1ed9t th\u01b0 m\u1ee5c con m\u1edbi <code>.\/src\/prisma<\/code> v\u1edbi hai t\u1ec7p: <code>prisma.module.ts<\/code> v\u00e0 <code>prisma.service.ts<\/code>. T\u1ec7p <strong>service<\/strong> (<code>prisma.service.ts<\/code>) s\u1ebd ch\u1ee9a \u0111o\u1ea1n m\u00e3 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/prisma\/prisma.service.ts\r\n\r\nimport { Injectable } from '@nestjs\/common';\r\nimport { PrismaClient } from '@prisma\/client';\r\n\r\n@Injectable()\r\nexport class PrismaService extends PrismaClient {}\r\n<\/pre>\r\n\r\n\r\n\r\n<p><strong>Prisma Module<\/strong> s\u1ebd ch\u1ecbu tr\u00e1ch nhi\u1ec7m t\u1ea1o m\u1ed9t instance <strong>singleton<\/strong> c\u1ee7a <code>PrismaService<\/code> v\u00e0 cho ph\u00e9p chia s\u1ebb service n\u00e0y trong to\u00e0n b\u1ed9 \u1ee9ng d\u1ee5ng c\u1ee7a b\u1ea1n. \u0110\u1ec3 l\u00e0m \u0111i\u1ec1u n\u00e0y, b\u1ea1n c\u1ea7n th\u00eam <code>PrismaService<\/code> v\u00e0o m\u1ea3ng <code>exports<\/code> trong t\u1ec7p <code>prisma.module.ts<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/prisma\/prisma.module.ts\r\n\r\nimport { Module } from '@nestjs\/common';\r\nimport { PrismaService } from '.\/prisma.service';\r\n\r\n@Module({\r\n  providers: [PrismaService],\r\n  exports: [PrismaService],\r\n})\r\nexport class PrismaModule {}<\/pre>\r\n\r\n\r\n\r\n<p>B\u00e2y gi\u1edd, b\u1ea5t k\u1ef3 module n\u00e0o import <strong>PrismaModule<\/strong> s\u1ebd c\u00f3 quy\u1ec1n truy c\u1eadp v\u00e0o <strong>PrismaService<\/strong> v\u00e0 c\u00f3 th\u1ec3 ti\u00eam (<strong>inject<\/strong>) n\u00f3 v\u00e0o c\u00e1c th\u00e0nh ph\u1ea7n ho\u1eb7c service c\u1ee7a ri\u00eang m\u00ecnh. \u0110\u00e2y l\u00e0 m\u1ed9t design parttern th\u00f4ng d\u1ee5ng trong c\u00e1c \u1ee9ng d\u1ee5ng <strong>NestJS<\/strong>.<\/p>\r\n\r\n\r\n\r\n<p>V\u1edbi b\u01b0\u1edbc n\u00e0y, b\u1ea1n \u0111\u00e3 ho\u00e0n th\u00e0nh vi\u1ec7c thi\u1ebft l\u1eadp <strong>Prisma<\/strong>! Gi\u1edd \u0111\u00e2y, b\u1ea1n c\u00f3 th\u1ec3 b\u1eaft \u0111\u1ea7u l\u00e0m vi\u1ec7c \u0111\u1ec3 x\u00e2y d\u1ef1ng <strong>REST API<\/strong>.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id=\"set-up-swagger\">Thi\u1ebft l\u1eadp Swagger<\/h2>\r\n\r\n\r\n\r\n<p><strong>Swagger<\/strong> l\u00e0 m\u1ed9t c\u00f4ng c\u1ee5 \u0111\u1ec3 t\u00e0i li\u1ec7u h\u00f3a API c\u1ee7a b\u1ea1n b\u1eb1ng c\u00e1ch s\u1eed d\u1ee5ng ti\u00eau chu\u1ea9n <strong>OpenAPI specification<\/strong>. <strong>NestJS<\/strong> c\u00f3 m\u1ed9t module d\u00e0nh ri\u00eang cho Swagger, v\u00e0 b\u1ea1n s\u1ebd s\u1eed d\u1ee5ng n\u00f3 \u0111\u1ec3 t\u00e0i li\u1ec7u h\u00f3a API c\u1ee7a m\u00ecnh.<\/p>\r\n\r\n\r\n\r\n<p>Swagger gi\u00fap b\u1ea1n t\u1ea1o t\u00e0i li\u1ec7u d\u1ec5 \u0111\u1ecdc v\u00e0 t\u01b0\u01a1ng t\u00e1c, cho ph\u00e9p b\u1ea1n v\u00e0 c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n kh\u00e1c c\u00f3 th\u1ec3 ki\u1ec3m tra v\u00e0 s\u1eed d\u1ee5ng API m\u1ed9t c\u00e1ch thu\u1eadn ti\u1ec7n m\u00e0 kh\u00f4ng c\u1ea7n ph\u1ea3i \u0111\u1ecdc qua to\u00e0n b\u1ed9 m\u00e3 ngu\u1ed3n ho\u1eb7c t\u00e0i li\u1ec7u d\u00e0i d\u00f2ng.<\/p>\r\n\r\n\r\n\r\n<p>B\u1eaft \u0111\u1ea7u b\u1eb1ng c\u00e1ch c\u00e0i \u0111\u1eb7t c\u00e1c th\u01b0 vi\u1ec7n c\u1ea7n thi\u1ebft:<\/p>\r\n\r\n\r\n\r\n<div class=\"hcb_wrap\">\r\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code>npm install --save @nestjs\/swagger swagger-ui-express\r\n<\/code><\/pre>\r\n<\/div>\r\n\r\n\r\n\r\n<p>M\u1edf t\u1ec7p <code>main.ts<\/code> v\u00e0 kh\u1edfi t\u1ea1o <strong>Swagger<\/strong> b\u1eb1ng c\u00e1ch s\u1eed d\u1ee5ng l\u1edbp <strong>SwaggerModule<\/strong>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/main.ts\r\n\r\nimport { NestFactory } from '@nestjs\/core';\r\nimport { AppModule } from '.\/app.module';\r\nimport { SwaggerModule, DocumentBuilder } from '@nestjs\/swagger';\r\n\r\nasync function bootstrap() {\r\n  const app = await NestFactory.create(AppModule);\r\n\r\n  const config = new DocumentBuilder()\r\n    .setTitle('MEVN Products Management')\r\n    .setDescription('MEVN Products Managemen API description')\r\n    .setVersion('0.1')\r\n    .build();\r\n\r\n  const document = SwaggerModule.createDocument(app, config);\r\n  SwaggerModule.setup('api', app, document);\r\n\r\n  await app.listen(3000);\r\n}\r\nbootstrap();<\/pre>\r\n\r\n\r\n\r\n<p>Trong khi \u1ee9ng d\u1ee5ng \u0111ang ch\u1ea1y, h\u00e3y m\u1edf tr\u00ecnh duy\u1ec7t c\u1ee7a b\u1ea1n v\u00e0 truy c\u1eadp v\u00e0o \u0111\u1ecba ch\u1ec9 <a href=\"http:\/\/localhost:3000\/api\" target=\"_blank\" rel=\"noopener\"><code>http:\/\/localhost:3000\/api<\/code><\/a>. B\u1ea1n s\u1ebd th\u1ea5y giao di\u1ec7n <strong>Swagger UI<\/strong>.<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"839\" height=\"393\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04140827\/Screenshot-2024-12-04-at-14.08.17.png\" alt=\"S\u1eed d\u1ee5ng Swagger \u0111\u1ec3 Documenets REST API\" class=\"wp-image-4426\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04140827\/Screenshot-2024-12-04-at-14.08.17.png 839w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04140827\/Screenshot-2024-12-04-at-14.08.17-300x141.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04140827\/Screenshot-2024-12-04-at-14.08.17-768x360.png 768w\" sizes=\"auto, (max-width: 839px) 100vw, 839px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id=\"implement-crud-operations-for-article-model\">Tri\u1ec3n khai REST API CRUD cho model Product:<\/h2>\r\n\r\n\r\n\r\n<p>Trong ph\u1ea7n n\u00e0y, b\u1ea1n s\u1ebd tri\u1ec3n khai c\u00e1c thao t\u00e1c <strong>Create<\/strong> (t\u1ea1o), <strong>Read<\/strong> (\u0111\u1ecdc), <strong>Update<\/strong> (c\u1eadp nh\u1eadt), v\u00e0 <strong>Delete<\/strong> (x\u00f3a) cho\u00a0 model <code>Product<\/code> v\u00e0 b\u1ea5t k\u1ef3 logic nghi\u1ec7p v\u1ee5 li\u00ean quan n\u00e0o.<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\"><strong>T\u1ea1o REST resources<\/strong><\/h3>\r\n\r\n\r\n\r\n<p>Tr\u01b0\u1edbc khi b\u1ea1n c\u00f3 th\u1ec3 tri\u1ec3n khai <strong>REST API<\/strong>, b\u1ea1n c\u1ea7n t\u1ea1o REST resouces cho m\u00f4 h\u00ecnh <code>Product<\/code>. \u0110i\u1ec1u n\u00e0y c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c th\u1ef1c hi\u1ec7n nhanh ch\u00f3ng b\u1eb1ng <strong>Nest CLI<\/strong>. H\u00e3y ch\u1ea1y l\u1ec7nh sau trong terminal c\u1ee7a b\u1ea1n:<\/p>\r\n\r\n\r\n\r\n<p>Sau khi tr\u1ea3 l\u1eddi c\u00e1c c\u00e2u h\u1ecfi t\u1eeb <strong>CLI<\/strong>:<\/p>\r\n\r\n\r\n\r\n<ol class=\"wp-block-list\">\r\n<li><strong>What name would you like to use for this resource (plural, e.g., &#8220;users&#8221;)?\u00a0 <\/strong>B\u1ea1n nh\u1eadp <code>products<\/code>.<\/li>\r\n\r\n\r\n\r\n<li><strong>What transport layer do you use? <\/strong>Ch\u1ecdn <code>REST API<\/code>.<\/li>\r\n\r\n\r\n\r\n<li><strong>Would you like to generate CRUD entry points? <\/strong>Ch\u1ecdn <code>Yes<\/code>.<\/li>\r\n<\/ol>\r\n\r\n\r\n\r\n<p>B\u1ea1n s\u1ebd th\u1ea5y m\u1ed9t th\u01b0 m\u1ee5c m\u1edbi c\u00f3 t\u00ean <code>src\/products<\/code> v\u1edbi t\u1ea5t c\u1ea3 m\u00e3 m\u1eabu (<strong>boilerplate<\/strong>) cho c\u00e1c endpoint <strong>REST<\/strong> c\u1ee7a b\u1ea1n.<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>B\u00ean trong t\u1ec7p <code>src\/products\/products.controller.ts<\/code>, b\u1ea1n s\u1ebd th\u1ea5y \u0111\u1ecbnh ngh\u0129a c\u1ee7a c\u00e1c route kh\u00e1c nhau (c\u00f2n g\u1ecdi l\u00e0 <strong>route handlers<\/strong>).<\/li>\r\n\r\n\r\n\r\n<li><strong>Business logic<\/strong> cho vi\u1ec7c x\u1eed l\u00fd t\u1eebng y\u00eau c\u1ea7u \u0111\u01b0\u1ee3c g\u00f3i g\u1ecdn trong t\u1ec7p <code>src\/products\/products.service.ts<\/code>. Hi\u1ec7n t\u1ea1i, t\u1ec7p n\u00e0y ch\u1ee9a c\u00e1c code demo.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p>N\u1ebfu b\u1ea1n m\u1edf l\u1ea1i trang <strong>Swagger API<\/strong>, b\u1ea1n s\u1ebd th\u1ea5y giao di\u1ec7n hi\u1ec3n th\u1ecb c\u00e1c endpoint CRUD cho <code>products<\/code>, cho ph\u00e9p b\u1ea1n ki\u1ec3m tra v\u00e0 t\u01b0\u01a1ng t\u00e1c v\u1edbi API c\u1ee7a m\u00ecnh tr\u1ef1c ti\u1ebfp th\u00f4ng qua Swagger.<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1899\" height=\"942\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30.png\" alt=\"Swagger API documents\" class=\"wp-image-4427\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30.png 1899w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30-300x149.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30-1024x508.png 1024w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30-768x381.png 768w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30-1536x762.png 1536w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/04142147\/Screenshot-2024-12-04-at-14.21.30-1568x778.png 1568w\" sizes=\"auto, (max-width: 1899px) 100vw, 1899px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<p><strong>SwaggerModule<\/strong> t\u00ecm ki\u1ebfm t\u1ea5t c\u1ea3 c\u00e1c decorator <code>@Body()<\/code>, <code>@Query()<\/code>, v\u00e0 <code>@Param()<\/code> tr\u00ean c\u00e1c <strong>route handlers<\/strong> \u0111\u1ec3 t\u1ef1 \u0111\u1ed9ng t\u1ea1o trang API n\u00e0y.<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">Th\u00eam <code>PrismaClient<\/code> v\u00e0o <code>Products<\/code> module<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u1ec3 truy c\u1eadp <code>PrismaClient<\/code> b\u00ean trong module <code>Products<\/code>, b\u1ea1n c\u1ea7n th\u00eam <code>PrismaModule<\/code> v\u00e0o ph\u1ea7n import c\u1ee7a <code>ProductsModule<\/code>. H\u00e3y th\u00eam c\u00e1c d\u00f2ng import sau v\u00e0o t\u1ec7p <code>products.module.ts<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.module.ts\r\nimport { Module } from '@nestjs\/common';\r\nimport { ProductsService } from '.\/products.service';\r\nimport { ProductsController } from '.\/products.controller';\r\nimport { PrismaModule } from 'src\/prisma\/prisma.module';\r\n\r\n@Module({\r\n  controllers: [ProductsController],\r\n  providers: [ProductsService],\r\n  imports: [PrismaModule],\r\n})\r\nexport class ProductsModule {}<\/pre>\r\n\r\n\r\n\r\n<p>B\u00e2y gi\u1edd b\u1ea1n c\u00f3 th\u1ec3 inject <code>PrismaService<\/code> v\u00e0o <code>ProductsService<\/code> v\u00e0 s\u1eed d\u1ee5ng n\u00f3 \u0111\u1ec3 truy c\u1eadp c\u01a1 s\u1edf d\u1eef li\u1ec7u. \u0110\u1ec3 l\u00e0m \u0111i\u1ec1u n\u00e0y, h\u00e3y th\u00eam m\u1ed9t <strong>constructor<\/strong> v\u00e0o t\u1ec7p <strong><code>products.service.ts<\/code><\/strong> nh\u01b0 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.service.ts\r\n\r\nimport { Injectable } from '@nestjs\/common';\r\nimport { CreateProductDto } from '.\/dto\/create-product.dto';\r\nimport { UpdateProductDto } from '.\/dto\/update-product.dto';\r\nimport { PrismaService } from 'src\/prisma\/prisma.service';\r\n\r\n@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n\r\n  \/\/ CRUD operations\r\n}<\/pre>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">\u0110\u1ecbnh ngh\u0129a <code>GET \/products<\/code> endpoint<\/h3>\r\n\r\n\r\n\r\n<p>Controller cho endpoint n\u00e0y c\u00f3 t\u00ean l\u00e0 <code>findAll<\/code>. Endpoint n\u00e0y s\u1ebd tr\u1ea3 v\u1ec1 t\u1ea5t c\u1ea3 <strong>products<\/strong> \u0111\u00e3 \u0111\u01b0\u1ee3c t\u1ea1o v\u1edbi c\u1edd l\u00e0 <code>published: true<\/code>\u00a0trong c\u01a1 s\u1edf d\u1eef li\u1ec7u. Controller <code>findAll<\/code> tr\u00f4ng nh\u01b0 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n\r\n@Get()\r\nfindAll() {\r\n   return this.productsService.findAll();\r\n}\r\n<\/pre>\r\n\r\n\r\n\r\n<p>B\u1ea1n c\u1ea7n c\u1eadp nh\u1eadt <code>ProductsService.findAll()<\/code> \u0111\u1ec3 tr\u1ea3 v\u1ec1 m\u1ed9t m\u1ea3ng t\u1ea5t c\u1ea3 c\u00e1c <code>product<\/code> \u0111\u00e3 \u0111\u01b0\u1ee3c published trong c\u01a1 s\u1edf d\u1eef li\u1ec7u:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"12\" data-enlighter-linenumbers=\"false\">\/\/ src\/products\/products.service.ts\r\n\r\n@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n  \r\n  create(createProductDto: CreateProductDto) {\r\n    return 'This action adds a new product';\r\n  }\r\n  \r\n  findAll() {\r\n    return this.prisma.product.findMany({ where: { published: true } });\r\n  }\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>Truy v\u1ea5n <code>findMany<\/code> s\u1ebd tr\u1ea3 v\u1ec1 t\u1ea5t c\u1ea3 c\u00e1c b\u1ea3n ghi <code>product<\/code> kh\u1edbp v\u1edbi \u0111i\u1ec1u ki\u1ec7n <code>where<\/code>.<\/p>\r\n\r\n\r\n\r\n<p>B\u1ea1n c\u00f3 th\u1ec3 ki\u1ec3m tra endpoint n\u00e0y b\u1eb1ng c\u00e1ch truy c\u1eadp v\u00e0o <a href=\"http:\/\/localhost:3000\/api\" target=\"_blank\" rel=\"noopener\">http:\/\/localhost:3000\/api<\/a> v\u00e0 nh\u1ea5p v\u00e0o dropdown menu <code>GET\/products<\/code>. Nh\u1ea5n <code>Try it out<\/code> r\u1ed3i <code>Execute<\/code> \u0111\u1ec3 xem k\u1ebft qu\u1ea3.<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1290\" height=\"919\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171408\/Screenshot-2024-12-05-at-17.13.47.png\" alt=\"Get products v\u1edbi Swagger\" class=\"wp-image-4451\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171408\/Screenshot-2024-12-05-at-17.13.47.png 1290w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171408\/Screenshot-2024-12-05-at-17.13.47-300x214.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171408\/Screenshot-2024-12-05-at-17.13.47-1024x730.png 1024w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/05171408\/Screenshot-2024-12-05-at-17.13.47-768x547.png 768w\" sizes=\"auto, (max-width: 1290px) 100vw, 1290px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd:<\/strong> B\u1ea1n c\u0169ng c\u00f3 th\u1ec3 ch\u1ea1y t\u1ea5t c\u1ea3 c\u00e1c request tr\u1ef1c ti\u1ebfp trong tr\u00ecnh duy\u1ec7t ho\u1eb7c th\u00f4ng qua m\u1ed9t REST client (nh\u01b0 <strong>Postman<\/strong>). <strong>Swagger<\/strong> c\u0169ng t\u1ef1 \u0111\u1ed9ng t\u1ea1o c\u00e1c l\u1ec7nh <strong>curl<\/strong> cho m\u1ed7i y\u00eau c\u1ea7u, trong tr\u01b0\u1eddng h\u1ee3p b\u1ea1n mu\u1ed1n ch\u1ea1y c\u00e1c y\u00eau c\u1ea7u HTTP trong terminal.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">\u0110\u1ecbnh ngh\u0129a <code>GET \/products\/drafts<\/code> endpoint<\/h3>\r\n<p>B\u1ea1n s\u1ebd \u0111\u1ecbnh ngh\u0129a m\u1ed9t route m\u1edbi \u0111\u1ec3 l\u1ea5y t\u1ea5t c\u1ea3 <code>products<\/code> ch\u01b0a \u0111\u01b0\u1ee3c xu\u1ea5t b\u1ea3n (<strong>unpublished<\/strong>). NestJS kh\u00f4ng t\u1ef1 \u0111\u1ed9ng t\u1ea1o \u00a0route handler cho endpoint n\u00e0y, v\u00ec v\u1eady b\u1ea1n c\u1ea7n vi\u1ebft n\u00f3 th\u1ee7 c\u00f4ng.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"12-15\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n\r\n@Controller('products')\r\nexport class ProductsController {\r\n  constructor(private readonly productsService: ProductsService) {}\r\n\r\n  @Post()\r\n  create(@Body() createProductDto: CreateProductDto) {\r\n    return this.productsService.create(createProductDto);\r\n  }\r\n\r\n  @Get('drafts')\r\n  findAllDrafts() {\r\n    return this.productsService.findDrafts();\r\n  }\r\n  \/\/...\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>Tr\u00ecnh so\u1ea1n th\u1ea3o c\u1ee7a b\u1ea1n s\u1ebd hi\u1ec3n th\u1ecb l\u1ed7i r\u1eb1ng kh\u00f4ng c\u00f3 h\u00e0m n\u00e0o t\u00ean l\u00e0 <code>productsService.findDrafts()<\/code>. \u0110\u1ec3 kh\u1eafc ph\u1ee5c l\u1ed7i n\u00e0y, b\u1ea1n c\u1ea7n tri\u1ec3n khai ph\u01b0\u01a1ng th\u1ee9c <code>findDrafts<\/code> trong <code>ProductsService<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"11-13\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.service.ts\r\n\r\n@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n  \r\n  create(createProductDto: CreateProductDto) {\r\n    return 'This action adds a new product';\r\n  }\r\n\r\n  findDrafts() {\r\n    return this.prisma.product.findMany({ where: { published: false } });\r\n  }\r\n\r\n  findAll() {\r\n    return this.prisma.product.findMany({ where: { published: true } });\r\n  }\r\n}<\/pre>\r\n\r\n\r\n\r\n<p><span>B\u00e2y gi\u1edd b\u1ea1n c\u00f3 th\u1ec3 truy c\u1eadp endpoint <\/span><code class=\"sc-ikkyvV cAsUNv\">GET \/products\/drafts<\/code> b\u1eb1ng c\u00e1ch load l\u1ea1i trang <span>Swagger\u00a0<\/span><a href=\"http:\/\/localhost:3000\/api\" class=\"sc-cwHqhk gzueGI\" target=\"_blank\" rel=\"noopener\">API page<\/a><span>.<\/span><\/p>\r\n\r\n\r\n\r\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\r\n<p><em><strong>L\u01b0u \u00fd<\/strong>: b\u1ea1n n\u00ean ki\u1ec3m tra t\u1eebng endpoint th\u00f4ng qua trang API Swagger sau khi ho\u00e0n t\u1ea5t qu\u00e1 tr\u00ecnh implement endpoint \u0111\u00f3.<\/em><\/p>\r\n<\/blockquote>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\">\u0110\u1ecbnh ngh\u0129a <code>GET \/products\/:id<\/code> endpoint<\/h3>\r\n\r\n\r\n\r\n<p>Handler cho route c\u1ee7a controller n\u00e0y c\u00f3 t\u00ean l\u00e0 <strong>findOne<\/strong>. N\u00f3 tr\u00f4ng nh\u01b0 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n\r\n@Get(':id')\r\n findOne(@Param('id') id: string) {\r\n   return this.productsService.findOne(+id);\r\n }<\/pre>\r\n\r\n\r\n\r\n<p>Route n\u00e0y ch\u1ea5p nh\u1eadn tham s\u1ed1 <code>id<\/code> \u0111\u1ed9ng, v\u00e0 tham s\u1ed1 n\u00e0y \u0111\u01b0\u1ee3c truy\u1ec1n v\u00e0o handler <code>findOne<\/code> c\u1ee7a controller. V\u00ec <code>Product<\/code> model c\u00f3 tr\u01b0\u1eddng <code>id<\/code> l\u00e0 ki\u1ec3u s\u1ed1 nguy\u00ean, n\u00ean tham s\u1ed1 <code>id<\/code> c\u1ea7n \u0111\u01b0\u1ee3c chuy\u1ec3n \u0111\u1ed5i th\u00e0nh ki\u1ec3u s\u1ed1 b\u1eb1ng c\u00e1ch s\u1eed d\u1ee5ng to\u00e1n t\u1eed <code>+<\/code>.<\/p>\r\n\r\n\r\n\r\n<p>B\u00e2y gi\u1edd, h\u00e3y c\u1eadp nh\u1eadt ph\u01b0\u01a1ng th\u1ee9c <code>findOne <\/code>trong <code>ProductsService<\/code> \u0111\u1ec3 tr\u1ea3 v\u1ec1 product v\u1edbi id \u0111\u00e3 cung c\u1ea5p:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"15-17\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.service.ts\r\n\r\n@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n  \r\n  create(createProductDto: CreateProductDto) {\r\n    return 'This action adds a new product';\r\n  }\r\n\r\n  findAll() {\r\n    return this.prisma.product.findMany({ where: { published: true } });\r\n  }\r\n\r\n  findOne(id: number) {\r\n    return this.prisma.product.findUnique({ where: { id } });\r\n  }\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>M\u1ed9t l\u1ea7n n\u1eefa, h\u00e3y ki\u1ec3m tra endpoint n\u00e0y b\u1eb1ng c\u00e1ch truy c\u1eadp v\u00e0o <a href=\"http:\/\/localhost:3000\/api\" target=\"_blank\" rel=\"noopener\"><code>http:\/\/localhost:3000\/api<\/code><\/a>. Nh\u1ea5p v\u00e0o menu dropdown <code>GET \/products\/{id}<\/code>. Nh\u1ea5n <code>Try it out<\/code>, th\u00eam m\u1ed9t gi\u00e1 tr\u1ecb h\u1ee3p l\u1ec7 v\u00e0o tham s\u1ed1 <code>id<\/code>, sau \u0111\u00f3 nh\u1ea5n <code>Execute<\/code> \u0111\u1ec3 xem k\u1ebft qu\u1ea3.<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1291\" height=\"954\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06092356\/Screenshot-2024-12-06-at-09.23.40.png\" alt=\"Enpoint GET \/products\/:id\" class=\"wp-image-4452\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06092356\/Screenshot-2024-12-06-at-09.23.40.png 1291w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06092356\/Screenshot-2024-12-06-at-09.23.40-300x222.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06092356\/Screenshot-2024-12-06-at-09.23.40-1024x757.png 1024w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06092356\/Screenshot-2024-12-06-at-09.23.40-768x568.png 768w\" sizes=\"auto, (max-width: 1291px) 100vw, 1291px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"define-post-articles-endpoint\"><span>\u0110\u1ecbnh ngh\u0129a <\/span><code class=\"sc-ikkyvV cAsUNv\">POST \/products<\/code><span>\u00a0<\/span>endpoint<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u00e2y l\u00e0 endpoint \u0111\u1ec3 t\u1ea1o m\u1edbi c\u00e1c <code>product<\/code>. Handler c\u1ee7a controller cho endpoint n\u00e0y c\u00f3 t\u00ean l\u00e0 <code>create<\/code>. N\u00f3 tr\u00f4ng nh\u01b0 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n@Post()\r\ncreate(@Body() createProductDto: CreateProductDto) {\r\n  return this.productsService.create(createProductDto);\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>L\u01b0u \u00fd r\u1eb1ng ph\u01b0\u01a1ng th\u1ee9c n\u00e0y mong \u0111\u1ee3i c\u00e1c tham s\u1ed1 c\u00f3 ki\u1ec3u <code>CreateProductDto<\/code> trong ph\u1ea7n request body. <strong>DTO (Data Transfer Object)<\/strong> l\u00e0 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng \u0111\u1ecbnh ngh\u0129a c\u00e1ch d\u1eef li\u1ec7u s\u1ebd \u0111\u01b0\u1ee3c g\u1eedi qua network. Hi\u1ec7n t\u1ea1i, <code>CreateProductDto<\/code> l\u00e0 m\u1ed9t l\u1edbp tr\u1ed1ng. B\u1ea1n s\u1ebd th\u00eam c\u00e1c thu\u1ed9c t\u00ednh v\u00e0o \u0111\u00f3 \u0111\u1ec3 x\u00e1c \u0111\u1ecbnh c\u1ea5u tr\u00fac c\u1ee7a ph\u1ea7n request body.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/src\/products\/dto\/create-product.dto.ts \r\n\r\nimport { ApiProperty } from '@nestjs\/swagger';\r\n\r\nexport class CreateProductDto {\r\n    @ApiProperty()\r\n    name: string;\r\n\r\n    @ApiProperty({ required: false })\r\n    description?: string;\r\n\r\n    @ApiProperty({ required: false, default: false })\r\n    published?: boolean = false;\r\n}\r\n<\/pre>\r\n\r\n\r\n\r\n<p><span><\/span>\u0110\u1ec3 c\u00e1c thu\u1ed9c t\u00ednh c\u1ee7a l\u1edbp `CreateProductDto` \u0111\u01b0\u1ee3c hi\u1ec3n th\u1ecb\u00a0<code>SwaggerModule<\/code> c\u1ea7n th\u00eam v\u00e0o c\u00e1c decorator <code>@ApiProperty<\/code> . Xem th\u00eam v\u1ec1 decorator trong <a href=\"https:\/\/docs.nestjs.com\/openapi\/types-and-parameters\" target=\"_blank\" rel=\"noopener\">t\u00e0i li\u1ec7u c\u1ee7a NestJS<\/a>.<\/p>\r\n\r\n\r\n\r\n<p><code>CreateProductDto<\/code> b\u00e2y gi\u1edd s\u1ebd \u0111\u01b0\u1ee3c hi\u1ec3n th\u1ecb tr\u00ean trang Swagger API d\u01b0\u1edbi ph\u1ea7n <strong>Schemas<\/strong>. C\u1ea5u tr\u00fac c\u1ee7a <code>UpdateProductDto<\/code> \u0111\u01b0\u1ee3c\u00a0 k\u1ebf th\u1eeba t\u1eeb <code>CreateProductDto<\/code>. V\u00ec v\u1eady, <code>UpdateProductDto<\/code> c\u0169ng \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a b\u00ean trong <strong>Swagger<\/strong>. \u0110i\u1ec1u n\u00e0y gi\u00fap Swagger hi\u1ec3u v\u00e0 m\u00f4 t\u1ea3 \u0111\u01b0\u1ee3c c\u00e1c y\u00eau c\u1ea7u c\u1ee7a API, t\u1eeb \u0111\u00f3 l\u00e0m cho t\u00e0i li\u1ec7u c\u1ee7a b\u1ea1n d\u1ec5 \u0111\u1ecdc v\u00e0 d\u1ec5 s\u1eed d\u1ee5ng h\u01a1n khi th\u1eed nghi\u1ec7m v\u00e0 t\u01b0\u01a1ng t\u00e1c v\u1edbi c\u00e1c endpoint.<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1298\" height=\"854\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06102059\/Screenshot-2024-12-06-at-10.20.41.png\" alt=\"DTO create, update\" class=\"wp-image-4455\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06102059\/Screenshot-2024-12-06-at-10.20.41.png 1298w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06102059\/Screenshot-2024-12-06-at-10.20.41-300x197.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06102059\/Screenshot-2024-12-06-at-10.20.41-1024x674.png 1024w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/06102059\/Screenshot-2024-12-06-at-10.20.41-768x505.png 768w\" sizes=\"auto, (max-width: 1298px) 100vw, 1298px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<p>B\u00e2y gi\u1edd h\u00e3y c\u1eadp nh\u1eadt ph\u01b0\u01a1ng th\u1ee9c <code>create<\/code> trong <code>ProductsService<\/code> \u0111\u1ec3 t\u1ea1o m\u1ed9t <strong>product<\/strong> m\u1edbi trong c\u01a1 s\u1edf d\u1eef li\u1ec7u.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"7-9\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.service.ts\r\n\r\n@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n  \r\n  create(createProductDto: CreateProductDto) {\r\n    return this.prisma.product.create({ data: CreateProductDto });\r\n  }\r\n\r\n  \/\/...\r\n}<\/pre>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"define-post-articles-endpoint\"><span>\u0110\u1ecbnh ng\u0129a <\/span><code class=\"sc-ikkyvV cAsUNv\">PATCH \/products\/:id<\/code><span>\u00a0<\/span>endpoint<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u00e2y l\u00e0 endpoint \u0111\u1ec3 c\u1eadp nh\u1eadt c\u00e1c <code>product<\/code> \u0111\u00e3 t\u1ed3n t\u1ea1i. Handler c\u1ee7a route cho endpoint n\u00e0y c\u00f3 t\u00ean l\u00e0 <code>update<\/code>. N\u00f3 tr\u00f4ng nh\u01b0 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n\r\n@Patch(':id')\r\nupdate(@Param('id') id: string, @Body() updateProductDto: UpdateProductDto) {\r\n  return this.productsService.update(+id, updateProductDto);\r\n}<\/pre>\r\n\r\n\r\n\r\n<p><code>UpdateProductDto<\/code> \u0111\u01b0\u1ee3c k\u1ebf th\u1eeba t\u1eeb <a href=\"https:\/\/docs.nestjs.com\/openapi\/mapped-types#partial\" target=\"_blank\" rel=\"noopener\"><code>PartialType<\/code><\/a> c\u1ee7a <code>CreateProductDto<\/code>, v\u00ec v\u1eady n\u00f3 c\u00f3 th\u1ec3 bao g\u1ed3m t\u1ea5t c\u1ea3 c\u00e1c thu\u1ed9c t\u00ednh c\u1ee7a <code>CreateProductDto<\/code>, nh\u01b0ng t\u1ea5t c\u1ea3 c\u00e1c thu\u1ed9c t\u00ednh n\u00e0y \u0111\u1ec1u l\u00e0 optional.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/dto\/update-product.dto.ts\r\n\r\nimport { PartialType } from '@nestjs\/swagger'; \r\nimport { CreateProductDto } from '.\/create-product.dto'; \r\nexport class UpdateProductDto extends PartialType(CreateProductDto) {}<\/pre>\r\n\r\n\r\n\r\n<p>T\u01b0\u01a1ng t\u1ef1, b\u1ea1n c\u1ea7n c\u1eadp nh\u1eadt ph\u01b0\u01a1ng th\u1ee9c t\u01b0\u01a1ng \u1ee9ng trong <code>ProductsService<\/code> \u0111\u1ec3 x\u1eed l\u00fd thao t\u00e1c n\u00e0y.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n\r\n  \/\/...\r\n\r\n  update(id: number, updateProductDto: UpdateProductDto) {\r\n    return this.prisma.product.update({\r\n      where: { id },\r\n      data: updateProductDto,\r\n    });\r\n  }\r\n\r\n  \/\/...\r\n\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>Ph\u01b0\u01a1ng th\u1ee9c <code>product.update<\/code> s\u1ebd c\u1ed1 g\u1eafng t\u00ecm m\u1ed9t b\u1ea3n ghi <code>Product<\/code> v\u1edbi <code>id<\/code> \u0111\u01b0\u1ee3c cung c\u1ea5p v\u00e0 c\u1eadp nh\u1eadt n\u00f3 v\u1edbi d\u1eef li\u1ec7u t\u1eeb <code>updateProductDto<\/code>.<\/p>\r\n\r\n\r\n\r\n<p>N\u1ebfu kh\u00f4ng t\u00ecm th\u1ea5y b\u1ea3n ghi <code>Product<\/code> n\u00e0o trong c\u01a1 s\u1edf d\u1eef li\u1ec7u v\u1edbi <code>id<\/code> \u0111\u00f3, Prisma s\u1ebd tr\u1ea3 v\u1ec1 m\u1ed9t l\u1ed7i. Trong nh\u1eefng tr\u01b0\u1eddng h\u1ee3p nh\u01b0 v\u1eady, API kh\u00f4ng cung c\u1ea5p th\u00f4ng b\u00e1o l\u1ed7i th\u00e2n thi\u1ec7n v\u1edbi ng\u01b0\u1eddi d\u00f9ng.M\u00ecnh s\u1ebd chia s\u1ebb c\u00e1ch x\u1eed l\u00fd l\u1ed7i v\u1edbi <code>NestJS<\/code> \u1edf m\u1ed9t b\u00e0i vi\u1ebft ti\u1ebfp theo. T\u1ea1m th\u1eddi, b\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ec3 l\u1ed7i hi\u1ec3n th\u1ecb nh\u01b0 m\u1eb7c \u0111\u1ecbnh ho\u1eb7c x\u1eed l\u00fd \u0111\u01a1n gi\u1ea3n b\u1eb1ng c\u00e1ch ki\u1ec3m tra k\u1ebft qu\u1ea3 tr\u01b0\u1edbc khi tr\u1ea3 v\u1ec1.<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"define-post-articles-endpoint\"><span>\u0110\u1ecbnh ng\u0129a <\/span><code class=\"sc-ikkyvV cAsUNv\">DELETE \/products\/:id<\/code><span>\u00a0<\/span>endpoint<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u00e2y l\u00e0 endpoint \u0111\u1ec3 x\u00f3a c\u00e1c <code>product<\/code> \u0111\u00e3 t\u1ed3n t\u1ea1i. Handler cho route c\u1ee7a endpoint n\u00e0y c\u00f3 t\u00ean l\u00e0 <code>remove<\/code>. N\u00f3 tr\u00f4ng nh\u01b0 sau:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n\r\n@Delete(':id')\r\nremove(@Param('id') id: string) {\r\n  return this.productsService.remove(+id);\r\n}<\/pre>\r\n\r\n\r\n\r\n<p><span>Gi\u1ed1ng nh\u01b0 tr\u01b0\u1edbc, h\u00e3y v\u00e0o <code>ProductsService<\/code> v\u00e0 c\u1eadp nh\u1eadt ph\u01b0\u01a1ng th\u1ee9c t\u01b0\u01a1ng \u1ee9ng:<\/span><\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"12-14\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.service.ts\r\n\r\n@Injectable()\r\nexport class ProductsService {\r\n  constructor(private prisma: PrismaService) {}\r\n  \r\n  create(createProductDto: CreateProductDto) {\r\n    return this.prisma.product.create({ data: CreateProductDto });\r\n  }\r\n\r\n  \/\/...\r\n  remove(id: number) {\r\n     return this.prisma.product.delete({ where: { id } });\r\n  }\r\n}\r\n\r\n\r\n<\/pre>\r\n\r\n\r\n\r\n<p>V\u1eady l\u00e0 ch\u00fang ta c\u0169ng \u0111\u00e3 implement xong endpoint cu\u1ed1i c\u00f9ng cho model <code>product<\/code>. Xin ch\u00fac m\u1eebng, API c\u1ee7a b\u1ea1n g\u1ea7n nh\u01b0 \u0111\u00e3 ho\u00e0n thi\u1ec7n! \ud83c\udf89<\/p>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"group-endpoints-together-in-swagger\">Nh\u00f3m c\u00e1c endpoints v\u1edbi nhau tr\u00ean Swagger<\/h3>\r\n\r\n\r\n\r\n<p>\u0110\u1ec3 nh\u00f3m t\u1ea5t c\u1ea3 c\u00e1c endpoint <code>products<\/code> l\u1ea1i v\u1edbi nhau trong <code>Swagger<\/code>, b\u1ea1n c\u1ea7n th\u00eam decorator <code>@ApiTags<\/code> v\u00e0o class <code>ProductsController<\/code>.\u00a0 Theo d\u00f5i code b\u00ean d\u01b0\u1edbi:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts \r\n\r\nimport { ApiTags } from '@nestjs\/swagger';\r\n\r\n@Controller('products') \r\n@ApiTags('products') \r\n\r\nexport class ProductsController {\r\n   \/\/ ... \r\n}<\/pre>\r\n\r\n\r\n\r\n<p>C\u00e1c endpoint c\u1ee7a <code>product<\/code> model s\u1ebd \u0111\u01b0\u1ee3c nh\u00f3m l\u1ea1i v\u1edbi nhau tr\u00ean <a href=\"http:\/\/localhost:3000\/api#\/products\" target=\"_blank\" rel=\"noopener\">API page<\/a>.<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1342\" height=\"921\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09090522\/Screenshot-2024-12-09-at-09.03.46.png\" alt=\"Group API in swagger\" class=\"wp-image-4468\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09090522\/Screenshot-2024-12-09-at-09.03.46.png 1342w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09090522\/Screenshot-2024-12-09-at-09.03.46-300x206.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09090522\/Screenshot-2024-12-09-at-09.03.46-1024x703.png 1024w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09090522\/Screenshot-2024-12-09-at-09.03.46-768x527.png 768w\" sizes=\"auto, (max-width: 1342px) 100vw, 1342px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<h2 class=\"wp-block-heading\">C\u1eadp nh\u1eadt response type cho Swagger<\/h2>\r\n\r\n\r\n\r\n<p>N\u1ebfu b\u1ea1n \u0111\u1ec3 \u00fd \u1edf ph\u1ea7n <code>Responses<\/code> c\u1ee7a m\u1ed7i endpoint, tab <code>Description<\/code> hi\u1ec7n \u0111ang r\u1ed7ng. \u0110\u1ec3 hi\u1ec3n th\u1ecb th\u00f4ng tin chi ti\u1ebft h\u01a1n trong tab <code>Description<\/code> c\u1ee7a m\u1ed7i endpoint tr\u00ean <strong>Swagger<\/strong>, b\u1ea1n c\u1ea7n \u0111\u1ecbnh ngh\u0129a m\u1ed9t\u00a0 entity m\u00e0 Swagger c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng \u0111\u1ec3 x\u00e1c \u0111\u1ecbnh c\u1ea5u tr\u00fac c\u1ee7a \u0111\u1ed1i t\u01b0\u1ee3ng tr\u1ea3 v\u1ec1. D\u01b0\u1edbi \u0111\u00e2y l\u00e0 c\u00e1ch c\u1eadp nh\u1eadt l\u1edbp <code>ProductEntity <\/code>trong t\u1ec7p <code>products.entity.ts<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/entities\/product.entity.ts\r\n\r\nimport { ApiProperty } from '@nestjs\/swagger';\r\nimport { Product } from '@prisma\/client';\r\n\r\nexport class ProductEntity implements Product {\r\n  @ApiProperty()\r\n  id: number;\r\n\r\n  @ApiProperty()\r\n  name: string;\r\n\r\n  @ApiProperty({ required: false, nullable: true })\r\n  description: string | null;\r\n\r\n  @ApiProperty()\r\n  published: boolean;\r\n\r\n  @ApiProperty()\r\n  createdAt: Date;\r\n\r\n  @ApiProperty()\r\n  updatedAt: Date;\r\n}<\/pre>\r\n\r\n\r\n\r\n<p>L\u1edbp <code>ProductEntity<\/code> b\u1ea1n v\u1eeba \u0111\u1ecbnh ngh\u0129a l\u00e0 m\u1ed9t implement c\u1ee7a ki\u1ec3u <code>Product<\/code> \u0111\u01b0\u1ee3c t\u1ea1o b\u1edfi <code>Prisma Client<\/code>, v\u1edbi c\u00e1c decorator <code>@ApiProperty<\/code> \u0111\u01b0\u1ee3c th\u00eam v\u00e0o cho m\u1ed7i thu\u1ed9c t\u00ednh.<\/p>\r\n\r\n\r\n\r\n<p>B\u00e2y gi\u1edd, b\u1ea1n s\u1ebd ch\u00fa th\u00edch c\u00e1c route handlers trong controller b\u1eb1ng c\u00e1c ki\u1ec3u ph\u1ea3n h\u1ed3i ch\u00ednh x\u00e1c. NestJS cung c\u1ea5p m\u1ed9t b\u1ed9 c\u00e1c decorator \u0111\u1ec3 l\u00e0m \u0111i\u1ec1u n\u00e0y, nh\u01b0 <code>@ApiOkResponse<\/code>, <code>@ApiCreatedResponse<\/code>, v\u00e0 <code>@ApiNotFoundResponse<\/code>, v.v.<\/p>\r\n\r\n\r\n\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"3,4,12,18,24,30,36,42\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ src\/products\/products.controller.ts\r\n\r\nimport { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs\/swagger';\r\nimport { ProductEntity } from '.\/entities\/product.entity';\r\n\r\n@Controller('products')\r\n@ApiTags('products')\r\nexport class ProductsController {\r\n  constructor(private readonly productsService: ProductsService) {}\r\n\r\n  @Post()\r\n  @ApiCreatedResponse({ type: ProductEntity })\r\n  create(@Body() createProductDto: CreateProductDto) {\r\n    return this.productsService.create(createProductDto);\r\n  }\r\n\r\n  @Get(':id')\r\n  @ApiOkResponse({ type: ProductEntity })\r\n  findOne(@Param('id') id: string) {\r\n    return this.productsService.findOne(+id);\r\n  }\r\n\r\n  @Get('drafts')\r\n  @ApiOkResponse({ type: ProductEntity, isArray: true })\r\n  findDrafts() {\r\n    return this.productsService.findDrafts();\r\n  }\r\n\r\n  @Get()\r\n  @ApiOkResponse({ type: ProductEntity, isArray: true })\r\n  findAll() {\r\n    return this.productsService.findAll();\r\n  }\r\n\r\n  @Patch(':id')\r\n  @ApiOkResponse({ type: ProductEntity })\r\n  update(@Param('id') id: string, @Body() updateProductDto: UpdateProductDto) {\r\n    return this.productsService.update(+id, updateProductDto);\r\n  }\r\n\r\n  @Delete(':id')\r\n  @ApiOkResponse({ type: ProductEntity })\r\n  remove(@Param('id') id: string) {\r\n    return this.productsService.remove(+id);\r\n  }\r\n}\r\n<\/pre>\r\n\r\n\r\n\r\n<p>B\u1ea1n \u0111\u00e3 th\u00eam <code>@ApiOkResponse<\/code> cho c\u00e1c endpoint <code>GET<\/code>, <code>PATCH<\/code>, v\u00e0 <code>DELETE<\/code>, v\u00e0 <code>@ApiCreatedResponse<\/code> cho c\u00e1c endpoint <code>POST<\/code>. Thu\u1ed9c t\u00ednh <code>type<\/code> \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 ch\u1ec9 \u0111\u1ecbnh ki\u1ec3u d\u1eef li\u1ec7u tr\u1ea3 v\u1ec1. T\u1ea5t c\u1ea3 c\u00e1c response decorator m\u00e0 NestJS cung c\u1ea5p \u0111\u1ec1u c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c t\u00ecm th\u1ea5y trong <a href=\"https:\/\/docs.nestjs.com\/openapi\/operations#responses\" target=\"_blank\" rel=\"noopener\">t\u00e0i li\u1ec7u NestJS<\/a>.<\/p>\r\n\r\n\r\n\r\n<p>B\u00e2y gi\u1edd, Swagger s\u1ebd hi\u1ec3n th\u1ecb chi ti\u1ebft r\u00f5 r\u00e0ng v\u1ec1 ki\u1ec3u ph\u1ea3n h\u1ed3i cho t\u1ea5t c\u1ea3 c\u00e1c endpoint tr\u00ean trang API, gi\u00fap c\u1ea3i thi\u1ec7n t\u00e0i li\u1ec7u v\u00e0 tr\u1ea3i nghi\u1ec7m ng\u01b0\u1eddi d\u00f9ng khi t\u01b0\u01a1ng t\u00e1c v\u1edbi API.<\/p>\r\n\r\n\r\n\r\n<p>N\u1ebfu b\u1ea1n ki\u1ec3m tra l\u1ea1i Swagger t\u1ea1i <a href=\"http:\/\/localhost:3000\/api\" target=\"_blank\" rel=\"noopener\"><code>http:\/\/localhost:3000\/api<\/code><\/a>, b\u1ea1n s\u1ebd th\u1ea5y c\u00e1c ki\u1ec3u ph\u1ea3n h\u1ed3i \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a \u0111\u1ea7y \u0111\u1ee7 v\u00e0 ch\u00ednh x\u00e1c cho t\u1eebng endpoint. \ud83c\udf89<\/p>\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1300\" height=\"959\" src=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09093518\/Screenshot-2024-12-09-at-09.35.04.png\" alt=\"Response Type\" class=\"wp-image-4472\" srcset=\"https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09093518\/Screenshot-2024-12-09-at-09.35.04.png 1300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09093518\/Screenshot-2024-12-09-at-09.35.04-300x221.png 300w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09093518\/Screenshot-2024-12-09-at-09.35.04-1024x755.png 1024w, https:\/\/mevn-public.s3-ap-southeast-1.amazonaws.com\/marketenterprise.vn\/wp-images\/2024\/12\/09093518\/Screenshot-2024-12-09-at-09.35.04-768x567.png 768w\" sizes=\"auto, (max-width: 1300px) 100vw, 1300px\" \/><\/figure>\r\n<\/div>\r\n\r\n\r\n<h2 class=\"wp-block-heading\">T\u00f3m t\u1eaft v\u00e0 nh\u1eadn x\u00e9t:<\/h2>\r\n\r\n\r\n\r\n<p>Ch\u00fac m\u1eebng b\u1ea1n! B\u1ea1n \u0111\u00e3 x\u00e2y d\u1ef1ng th\u00e0nh c\u00f4ng m\u1ed9t <strong>REST API c\u01a1 b\u1ea3n<\/strong> b\u1eb1ng <strong>NestJS<\/strong>. Trong su\u1ed1t h\u01b0\u1edbng d\u1eabn n\u00e0y, b\u1ea1n \u0111\u00e3:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>X\u00e2y d\u1ef1ng m\u1ed9t <strong>REST API<\/strong> v\u1edbi <strong>NestJS<\/strong>.<\/li>\r\n\r\n\r\n\r\n<li>T\u00edch h\u1ee3p <strong>Prisma<\/strong> m\u1ed9t c\u00e1ch m\u01b0\u1ee3t m\u00e0 v\u00e0o d\u1ef1 \u00e1n <strong>NestJS<\/strong>.<\/li>\r\n\r\n\r\n\r\n<li>T\u00e0i li\u1ec7u h\u00f3a <strong>REST API<\/strong> c\u1ee7a b\u1ea1n b\u1eb1ng <strong>Swagger<\/strong> v\u00e0 <strong>OpenAPI<\/strong>.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p>M\u1ed9t b\u00e0i h\u1ecdc quan tr\u1ecdng t\u1eeb h\u01b0\u1edbng d\u1eabn n\u00e0y l\u00e0 vi\u1ec7c x\u00e2y d\u1ef1ng <strong>REST API<\/strong> v\u1edbi <strong>NestJS<\/strong> v\u00e0 <strong>Prisma<\/strong> r\u1ea5t d\u1ec5 d\u00e0ng. \u0110\u00e2y l\u00e0 m\u1ed9t <strong>stack<\/strong> v\u00f4 c\u00f9ng hi\u1ec7u qu\u1ea3 \u0111\u1ec3 nhanh ch\u00f3ng ph\u00e1t tri\u1ec3n c\u00e1c \u1ee9ng d\u1ee5ng backend c\u00f3 c\u1ea5u tr\u00fac t\u1ed1t, an to\u00e0n v\u1ec1 ki\u1ec3u d\u1eef li\u1ec7u (<strong>type-safe<\/strong>) v\u00e0 d\u1ec5 b\u1ea3o tr\u00ec. \ud83d\ude80<\/p>\r\n\r\n\r\n\r\n<p>B\u00e0i vi\u1ebft \u0111\u01b0\u1ee3c tham kh\u1ea3o t\u1eeb m\u1ed9t s\u1ed1 ngu\u1ed3n:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><a href=\"https:\/\/www.prisma.io\/blog\/nestjs-prisma-rest-api-7D056s1BmOL0#create-a-postgresql-instance\" target=\"_blank\" rel=\"noopener\">Chu\u1ed7i<\/a> b\u00e0i h\u01b0\u1edbng d\u1eabn c\u1ee7a t\u00e1c gi\u1ea3 <a href=\"https:\/\/twitter.com\/tasinishmam\" target=\"_blank\" rel=\"noopener\">Tasin Ishmam<\/a> v\u1ec1 x\u00e2y d\u1ef1ng REST API, t\u1eeb c\u01a1 b\u1ea3n \u0111\u1ebfn validation, error handling, relational data, authentication&#8230;<\/li>\r\n\r\n\r\n\r\n<li>Trang t\u00e0i li\u1ec7u c\u1ee7a <a href=\"https:\/\/docs.nestjs.com\/\" target=\"_blank\" rel=\"noopener\">NestJS<\/a><\/li>\r\n\r\n\r\n\r\n<li>Trang t\u00e0i li\u1ec7u c\u1ee7a <a href=\"https:\/\/www.prisma.io\/docs\" target=\"_blank\" rel=\"noopener\">Prisma<\/a><\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p>N\u1ebfu c\u00f3 th\u1eddi gian m\u00ecnh s\u1ebd Vi\u1ec7t h\u00f3a c\u00e1c b\u00e0i vi\u1ebft ti\u1ebfp theo c\u1ee7a t\u00e1c gi\u1ea3 \u0111\u1ec3 c\u00e1c b\u1ea1n c\u00f3 th\u1ec3 d\u1ec5 d\u00e0ng t\u1ea1o n\u00ean m\u1ed9t \u1ee9ng d\u1ee5ng REST API BACKEND ho\u00e0n ch\u1ec9nh. H\u00e3y theo d\u00f5i v\u00e0 ch\u1edd \u0111\u00f3n nh\u00e9!<\/p>\r\n","protected":false},"excerpt":{"rendered":"<p>NestJS l\u00e0 m\u1ed9t trong nh\u1eefng framework n\u1ed5i b\u1eadt c\u1ee7a Node.js v\u00e0 g\u1ea7n \u0111\u00e2y \u0111\u00e3 nh\u1eadn \u0111\u01b0\u1ee3c r\u1ea5t nhi\u1ec1u s\u1ef1 y\u00eau th\u00edch v\u00e0 quan t\u00e2m t\u1eeb c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n. Trong qu\u00e1 tr\u00ecnh t\u00ecm ki\u1ebfm t\u00e0i li\u1ec7u m\u00ecnh c\u00f3 \u0111\u1ecdc \u0111\u01b0\u1ee3c m\u1ed9t chu\u1ed7i b\u00e0i vi\u1ebft kh\u00e1 hay v\u1ec1 c\u00e1ch x\u00e2y d\u1ef1ng m\u1ed9t \u1ee9ng d\u1ee5ng REST API [&hellip;]<\/p>\n","protected":false},"author":60,"featured_media":4481,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[137,199,146,198,22,102],"class_list":["post-4393","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technology","tag-huong-dan","tag-postgresql","tag-prisma","tag-restapi","tag-technical","tag-typescript"],"_links":{"self":[{"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/posts\/4393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/users\/60"}],"replies":[{"embeddable":true,"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/comments?post=4393"}],"version-history":[{"count":0,"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/posts\/4393\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/media\/4481"}],"wp:attachment":[{"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/media?parent=4393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/categories?post=4393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.marketenterprise.vn\/blog\/wp-json\/wp\/v2\/tags?post=4393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}