From d2e68dff5c92fd39c17765595254716a32014d2a Mon Sep 17 00:00:00 2001 From: sungmin Date: Wed, 11 Dec 2024 09:48:57 +0900 Subject: [PATCH] migration --- app.js | 30 +++-- data/seed.js | 28 +++-- package-lock.json | 111 +++++++++++++++++- package.json | 7 +- .../migration.sql | 25 ++++ prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 34 ++++++ structs.js | 14 +++ 8 files changed, 224 insertions(+), 28 deletions(-) create mode 100644 prisma/migrations/20241209095510_first_migrate_add_product_and_article/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 structs.js diff --git a/app.js b/app.js index 7f0b6bc..a27f0fd 100644 --- a/app.js +++ b/app.js @@ -1,11 +1,11 @@ import express from "express"; -import { Product } from "./models/product.js"; -import mongoose from "mongoose"; +import { PrismaClient, Prisma } from "@prisma/client"; +import { assert } from "superstruct"; import * as dotenv from "dotenv"; import cors from "cors"; - +import { CreateProduct, PatchProdcut } from "./structs.js"; dotenv.config(); - +const prisma = new PrismaClient(); const app = express(); app.use( @@ -16,6 +16,7 @@ app.use( ], }) ); +app.use(express.json()); const asyncHandler = (handler) => { return async (req, res) => { @@ -37,8 +38,6 @@ const asyncHandler = (handler) => { }; }; -app.use(express.json()); - app.get( "/products", asyncHandler(async (req, res) => { @@ -68,13 +67,14 @@ app.get( } : {}; - const products = await Product.find(search) + const products = await prisma.product + .find(search) .select("name price createdAt favoriteCount") .sort(sortOption) .skip(offset) .limit(pageSize); - const totalCount = await Product.countDocuments(search); + const totalCount = await prisma.product.countDocuments(search); res.send({ list: products, totalCount }); }) @@ -84,7 +84,7 @@ app.get( "/products/:id", asyncHandler(async (req, res) => { const id = req.params.id; - const product = await Product.findById(id).select("-updatedAt"); + const product = await prisma.product.findById(id).select("-updatedAt"); res.send(product); }) ); @@ -92,7 +92,8 @@ app.get( app.post( "/products", asyncHandler(async (req, res) => { - const product = await Product.create(req.body); + assert(req.body, CreateProduct); + const product = await prisma.product.create(req.body); res.status(201).send(product); }) ); @@ -100,8 +101,9 @@ app.post( app.patch( "/products/:id", asyncHandler(async (req, res) => { + assert(req.body, PatchProdcut); const id = req.params.id; - const product = await Product.findById(id); + const product = await prisma.product.findById(id); Object.keys(req.body).forEach((key) => { product[key] = req.body[key]; }); @@ -116,15 +118,11 @@ app.delete( "/products/:id", asyncHandler(async (req, res) => { const id = req.params.id; - await Product.findByIdAndDelete(id); + await prisma.product.findByIdAndDelete(id); res.sendStatus(204); }) ); -mongoose - .connect(process.env.DATABASE_URL) - .then(() => console.log("Connected to DB")); - app.listen(process.env.PORT || 3000, () => { console.log(`Server started`); }); diff --git a/data/seed.js b/data/seed.js index 3143bdd..8e6dfbe 100644 --- a/data/seed.js +++ b/data/seed.js @@ -1,13 +1,23 @@ -import mongoose from "mongoose"; import products from "./mock.js"; -import { Product } from "../models/product.js"; -import { config } from "dotenv"; +import { PrismaClient } from "@prisma/client"; -config(); +const prisma = new PrismaClient(); -await mongoose.connect(process.env.DATABASE_URL); +async function main() { + try { + await prisma.product.deleteMany({}); + await prisma.product.createMany({ + data: products, + skipDuplicates: true, + }); + } catch (e) { + throw e; + } finally { + await prisma.$disconnect(); + } +} -await Product.deleteMany({}); -await Product.insertMany(products); - -mongoose.connection.close(); +// 실행 +main().catch((e) => { + process.exit(1); +}); diff --git a/package-lock.json b/package-lock.json index de4bc8f..88a4ede 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@prisma/client": "^6.0.1", "cors": "^2.8.5", "dotenv": "^16.4.7", "express": "^4.21.2", - "mongoose": "^7.8.3" + "is-email": "^1.0.2", + "is-uuid": "^1.0.2", + "mongoose": "^7.8.3", + "prisma": "^6.0.1", + "superstruct": "^2.0.2" }, "devDependencies": { "nodemon": "^3.1.7" @@ -28,6 +33,69 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@prisma/client": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.0.1.tgz", + "integrity": "sha512-60w7kL6bUxz7M6Gs/V+OWMhwy94FshpngVmOY05TmGD0Lhk+Ac0ZgtjlL6Wll9TD4G03t4Sq1wZekNVy+Xdlbg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.0.1.tgz", + "integrity": "sha512-jQylgSOf7ibTVxqBacnAlVGvek6fQxJIYCQOeX2KexsfypNzXjJQSS2o5s+Mjj2Np93iSOQUaw6TvPj8syhG4w==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.0.1.tgz", + "integrity": "sha512-4hxzI+YQIR2uuDyVsDooFZGu5AtixbvM2psp+iayDZ4hRrAHo/YwgA17N23UWq7G6gRu18NvuNMb48qjP3DPQw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.0.1", + "@prisma/engines-version": "5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e", + "@prisma/fetch-engine": "6.0.1", + "@prisma/get-platform": "6.0.1" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e.tgz", + "integrity": "sha512-JmIds0Q2/vsOmnuTJYxY4LE+sajqjYKhLtdOT6y4imojqv5d/aeVEfbBGC74t8Be1uSp0OP8lxIj2OqoKbLsfQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.0.1.tgz", + "integrity": "sha512-T36bWFVGeGYYSyYOj9d+O9G3sBC+pAyMC+jc45iSL63/Haq1GrYjQPgPMxrEj9m739taXrupoysRedQ+VyvM/Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.0.1", + "@prisma/engines-version": "5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e", + "@prisma/get-platform": "6.0.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.0.1.tgz", + "integrity": "sha512-zspC9vlxAqx4E6epMPMLLBMED2VD8axDe8sPnquZ8GOsn6tiacWK0oxrGK4UAHYzYUVuMVUApJbdXB2dFpLhvg==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.0.1" + } + }, "node_modules/@types/node": { "version": "22.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", @@ -491,7 +559,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -692,6 +759,12 @@ "node": ">=8" } }, + "node_modules/is-email": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-email/-/is-email-1.0.2.tgz", + "integrity": "sha512-UojUgD2EhDTBQ2SGKwrK9edce5phRzgLsP+V5+Uu2Swi+uvjVXgH3zduM3HhT9iaC/9Kq19/TYUbP0jPoi6ioA==", + "license": "SEE LICENSE IN LICENSE" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -725,6 +798,12 @@ "node": ">=0.12.0" } }, + "node_modules/is-uuid": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-uuid/-/is-uuid-1.0.2.tgz", + "integrity": "sha512-tCByphFcJgf2qmiMo5hMCgNAquNSagOetVetDvBXswGkNfoyEMvGH1yDlF8cbZbKnbVBr4Y5/rlpMz9umxyBkQ==", + "license": "MIT" + }, "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", @@ -1083,6 +1162,25 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prisma": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.0.1.tgz", + "integrity": "sha512-CaMNFHkf+DDq8zq3X/JJsQ4Koy7dyWwwtOKibkT/Am9j/tDxcfbg7+lB1Dzhx18G/+RQCMgjPYB61bhRqteNBQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "6.0.1" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1366,6 +1464,15 @@ "node": ">= 0.8" } }, + "node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/package.json b/package.json index 371f88b..17bbb46 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,15 @@ "license": "ISC", "description": "", "dependencies": { + "@prisma/client": "^6.0.1", "cors": "^2.8.5", "dotenv": "^16.4.7", "express": "^4.21.2", - "mongoose": "^7.8.3" + "is-email": "^1.0.2", + "is-uuid": "^1.0.2", + "mongoose": "^7.8.3", + "prisma": "^6.0.1", + "superstruct": "^2.0.2" }, "devDependencies": { "nodemon": "^3.1.7" diff --git a/prisma/migrations/20241209095510_first_migrate_add_product_and_article/migration.sql b/prisma/migrations/20241209095510_first_migrate_add_product_and_article/migration.sql new file mode 100644 index 0000000..98bb2ac --- /dev/null +++ b/prisma/migrations/20241209095510_first_migrate_add_product_and_article/migration.sql @@ -0,0 +1,25 @@ +-- CreateTable +CREATE TABLE "Product" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "price" INTEGER NOT NULL, + "tags" TEXT[], + "favoriteCount" INTEGER NOT NULL, + "images" TEXT[], + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Article" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Article_pkey" PRIMARY KEY ("id") +); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..9d757aa --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,34 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Product { + id String @id @default(uuid()) + name String + description String + price Int + tags String[] + favoriteCount Int + images String[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Article { + id String @id @default(uuid()) + title String + content String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/structs.js b/structs.js new file mode 100644 index 0000000..221fc3e --- /dev/null +++ b/structs.js @@ -0,0 +1,14 @@ +import * as s from "superstruct"; +import isEmail from "is-email"; +import isUuid from "is-uuid"; + +export const CreateProduct = s.object({ + name: s.size(s.string(), 1, 10), + description: s.size(s.string(), 10, 100), + price: s.min(s.number(), 0), + tags: s.array(s.size(s.string(), 1, 5)), + favoriteCount: s.optional(s.min(s.number(), 0)), + images: s.optional(s.array(s.string())), +}); + +const PatchProdcut = s.partial(CreateProduct);