From 246b2254b74ee7d7dc6462ef87ec40765ab39ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stas=CC=81kiewicz?= Date: Fri, 5 Jul 2024 14:25:10 +0200 Subject: [PATCH 1/8] chore: setup drizzle --- .../common_nestjs_remix/apps/api/.env.example | 1 + .../common_nestjs_remix/apps/api/.prettierrc | 4 +- .../apps/api/drizzle.config.ts | 12 + .../common_nestjs_remix/apps/api/package.json | 6 + .../apps/api/src/app.module.ts | 31 +- .../api/src/common/configuration/database.ts | 17 + .../apps/api/src/storage/schema/index.ts | 7 + .../apps/api/src/storage/schema/utils.ts | 27 + .../common_nestjs_remix/docker-compose.yml | 15 + examples/common_nestjs_remix/pnpm-lock.yaml | 713 +++++++++++++++++- 10 files changed, 812 insertions(+), 21 deletions(-) create mode 100644 examples/common_nestjs_remix/apps/api/.env.example create mode 100644 examples/common_nestjs_remix/apps/api/drizzle.config.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/common/configuration/database.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/storage/schema/utils.ts create mode 100644 examples/common_nestjs_remix/docker-compose.yml diff --git a/examples/common_nestjs_remix/apps/api/.env.example b/examples/common_nestjs_remix/apps/api/.env.example new file mode 100644 index 0000000..01ec508 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/.env.example @@ -0,0 +1 @@ +DATABASE_URL="" diff --git a/examples/common_nestjs_remix/apps/api/.prettierrc b/examples/common_nestjs_remix/apps/api/.prettierrc index dcb7279..f3bcd4c 100644 --- a/examples/common_nestjs_remix/apps/api/.prettierrc +++ b/examples/common_nestjs_remix/apps/api/.prettierrc @@ -1,4 +1,4 @@ { - "singleQuote": true, + "singleQuote": false, "trailingComma": "all" -} \ No newline at end of file +} diff --git a/examples/common_nestjs_remix/apps/api/drizzle.config.ts b/examples/common_nestjs_remix/apps/api/drizzle.config.ts new file mode 100644 index 0000000..f1df67d --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/drizzle.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + schema: "./src/storage/schema", + out: "./src/storage/migrations", + dialect: "postgresql", + dbCredentials: { + url: "postgres://postgres:guidebook@localhost:5432/guidebook", + }, + verbose: true, + strict: true, +}); diff --git a/examples/common_nestjs_remix/apps/api/package.json b/examples/common_nestjs_remix/apps/api/package.json index da277e0..8cd3728 100644 --- a/examples/common_nestjs_remix/apps/api/package.json +++ b/examples/common_nestjs_remix/apps/api/package.json @@ -20,9 +20,15 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@knaadh/nestjs-drizzle-postgres": "^1.0.0", "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@sinclair/typebox": "^0.32.34", + "drizzle-kit": "^0.22.8", + "drizzle-orm": "^0.31.2", + "postgres": "^3.4.4", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/examples/common_nestjs_remix/apps/api/src/app.module.ts b/examples/common_nestjs_remix/apps/api/src/app.module.ts index 8662803..9743d10 100644 --- a/examples/common_nestjs_remix/apps/api/src/app.module.ts +++ b/examples/common_nestjs_remix/apps/api/src/app.module.ts @@ -1,9 +1,32 @@ -import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { Module } from "@nestjs/common"; +import { AppController } from "./app.controller"; +import { AppService } from "./app.service"; +import { DrizzlePostgresModule } from "@knaadh/nestjs-drizzle-postgres"; +import database from "./common/configuration/database"; +import { ConfigModule, ConfigService } from "@nestjs/config"; +import * as schema from "./storage/schema"; @Module({ - imports: [], + imports: [ + ConfigModule.forRoot({ + load: [database], + isGlobal: true, + }), + DrizzlePostgresModule.registerAsync({ + tag: "DB", + useFactory(configService: ConfigService) { + return { + postgres: { + url: configService.get("database.url")!, + }, + config: { + schema: { ...schema }, + }, + }; + }, + inject: [ConfigService], + }), + ], controllers: [AppController], providers: [AppService], }) diff --git a/examples/common_nestjs_remix/apps/api/src/common/configuration/database.ts b/examples/common_nestjs_remix/apps/api/src/common/configuration/database.ts new file mode 100644 index 0000000..d555fe0 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/common/configuration/database.ts @@ -0,0 +1,17 @@ +import { registerAs } from "@nestjs/config"; +import { Static, Type } from "@sinclair/typebox"; +import { Value } from "@sinclair/typebox/value"; + +const schema = Type.Object({ + url: Type.String(), +}); + +type DatabaseConfig = Static; + +export default registerAs("database", (): DatabaseConfig => { + const values = { + url: process.env.DATABASE_URL, + }; + + return Value.Decode(schema, values); +}); diff --git a/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts b/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts new file mode 100644 index 0000000..15287d9 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts @@ -0,0 +1,7 @@ +import { pgTable } from "drizzle-orm/pg-core"; +import { id, timestamps } from "./utils"; + +export const users = pgTable("users", { + ...id, + ...timestamps, +}); diff --git a/examples/common_nestjs_remix/apps/api/src/storage/schema/utils.ts b/examples/common_nestjs_remix/apps/api/src/storage/schema/utils.ts new file mode 100644 index 0000000..bc97867 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/storage/schema/utils.ts @@ -0,0 +1,27 @@ +import { sql } from "drizzle-orm"; +import { timestamp, uuid } from "drizzle-orm/pg-core"; + +export const id = { + id: uuid("id") + .default(sql`gen_random_uuid()`) + .primaryKey(), +}; + +export const timestamps = { + createdAt: timestamp("created_at", { + mode: "string", + withTimezone: true, + precision: 3, + }) + .default(sql`CURRENT_TIMESTAMP`) + .notNull(), + + updatedAt: timestamp("updated_at", { + mode: "string", + withTimezone: true, + precision: 3, + }) + .default(sql`CURRENT_TIMESTAMP`) + .notNull() + .$onUpdate(() => sql`CURRENT_TIMESTAMP`), +}; diff --git a/examples/common_nestjs_remix/docker-compose.yml b/examples/common_nestjs_remix/docker-compose.yml new file mode 100644 index 0000000..dd14984 --- /dev/null +++ b/examples/common_nestjs_remix/docker-compose.yml @@ -0,0 +1,15 @@ +services: + project-db: + image: postgres:16-alpine + restart: always + environment: + POSTGRES_PASSWORD: guidebook + POSTGRES_DB: guidebook + volumes: + - guidebook-db-data:/var/lib/postgresql/data + ports: + - 5432:5432 + +volumes: + guidebook-db-data: + driver: local diff --git a/examples/common_nestjs_remix/pnpm-lock.yaml b/examples/common_nestjs_remix/pnpm-lock.yaml index e76e2b1..7515f15 100644 --- a/examples/common_nestjs_remix/pnpm-lock.yaml +++ b/examples/common_nestjs_remix/pnpm-lock.yaml @@ -23,15 +23,33 @@ importers: apps/api: dependencies: + '@knaadh/nestjs-drizzle-postgres': + specifier: ^1.0.0 + version: 1.0.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(drizzle-orm@0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1))(postgres@3.4.4) '@nestjs/common': specifier: ^10.0.0 version: 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/config': + specifier: ^3.2.3 + version: 3.2.3(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(rxjs@7.8.1) '@nestjs/core': specifier: ^10.0.0 version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8) + '@sinclair/typebox': + specifier: ^0.32.34 + version: 0.32.34 + drizzle-kit: + specifier: ^0.22.8 + version: 0.22.8 + drizzle-orm: + specifier: ^0.31.2 + version: 0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1) + postgres: + specifier: ^3.4.4 + version: 3.4.4 reflect-metadata: specifier: ^0.2.0 version: 0.2.2 @@ -41,7 +59,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.0.0 - version: 10.3.2 + version: 10.3.2(esbuild@0.19.12) '@nestjs/schematics': specifier: ^10.0.0 version: 10.1.1(chokidar@3.6.0)(typescript@5.3.3) @@ -89,10 +107,10 @@ importers: version: 6.3.4 ts-jest: specifier: ^29.1.0 - version: 29.1.2(@babel/core@7.23.3)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.3))(jest@29.7.0(@types/node@20.11.24)(ts-node@10.9.1(@types/node@20.11.24)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.2(@babel/core@7.23.3)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.3))(esbuild@0.19.12)(jest@29.7.0(@types/node@20.11.24)(ts-node@10.9.1(@types/node@20.11.24)(typescript@5.3.3)))(typescript@5.3.3) ts-loader: specifier: ^9.4.3 - version: 9.5.1(typescript@5.3.3)(webpack@5.90.1) + version: 9.5.1(typescript@5.3.3)(webpack@5.90.1(esbuild@0.19.12)) ts-node: specifier: ^10.9.1 version: 10.9.1(@types/node@20.11.24)(typescript@5.3.3) @@ -501,6 +519,7 @@ packages: '@babel/parser@7.23.3': resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==} engines: {node: '>=6.0.0'} + hasBin: true '@babel/parser@7.24.5': resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} @@ -675,6 +694,18 @@ packages: '@emotion/hash@0.9.1': resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.20.2': resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} engines: {node: '>=12'} @@ -687,6 +718,18 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.20.2': resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} @@ -699,6 +742,18 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.20.2': resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} @@ -711,6 +766,18 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.20.2': resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} @@ -723,6 +790,18 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.20.2': resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} @@ -735,6 +814,18 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.20.2': resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} @@ -747,6 +838,18 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.20.2': resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} @@ -759,6 +862,18 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.20.2': resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} @@ -771,6 +886,18 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.20.2': resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} @@ -783,6 +910,18 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.20.2': resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} @@ -795,6 +934,18 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.20.2': resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} @@ -807,6 +958,18 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.20.2': resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} @@ -819,6 +982,18 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.20.2': resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} @@ -831,6 +1006,18 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.20.2': resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} @@ -843,6 +1030,18 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.20.2': resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} engines: {node: '>=12'} @@ -855,6 +1054,18 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.20.2': resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} @@ -867,6 +1078,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.20.2': resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} @@ -879,6 +1102,18 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.20.2': resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} @@ -891,6 +1126,18 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.20.2': resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} @@ -903,6 +1150,18 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.20.2': resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} @@ -915,6 +1174,18 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.20.2': resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} @@ -927,6 +1198,18 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.20.2': resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} @@ -939,6 +1222,18 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.20.2': resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} @@ -1090,6 +1385,13 @@ packages: '@jspm/core@2.0.1': resolution: {integrity: sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==} + '@knaadh/nestjs-drizzle-postgres@1.0.0': + resolution: {integrity: sha512-r3elSMh+M3ndJw1EJqYJWK0tqPo0zz+AURkLr/OoOws3wWKTyT0GQsKZTBpPytdZnbHqXPHh5ExsPNEc7eJ8Pw==} + peerDependencies: + '@nestjs/common': '>=9.0.0' + drizzle-orm: '>=0.28.6' + postgres: '>=3' + '@ljharb/through@2.3.13': resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} engines: {node: '>= 0.4'} @@ -1127,6 +1429,12 @@ packages: class-validator: optional: true + '@nestjs/config@3.2.3': + resolution: {integrity: sha512-p6yv/CvoBewJ72mBq4NXgOAi2rSQNWx3a+IMJLVKS2uiwFCOQQuiIatGwq6MRjXV3Jr+B41iUO8FIf4xBrZ4/w==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + rxjs: ^7.1.0 + '@nestjs/core@10.3.8': resolution: {integrity: sha512-AxF4tpYLDNn5Wfb3C4bNaaHJ4pREH5FJrSisR2A5zkYpQFORFs0Tc36lOFPMwBTy8Iv2wUwWLUVc5ftBnxEv4w==} peerDependencies: @@ -1376,6 +1684,9 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sinclair/typebox@0.32.34': + resolution: {integrity: sha512-a3Z3ytYl6R/+7ldxx04PO1semkwWlX/8pTqxsPw4quIcIXDFPZhOc1Wx8azWmkU26ccK3mHwcWenn0avNgAKQg==} + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1407,9 +1718,11 @@ packages: '@turbo/gen@1.12.4': resolution: {integrity: sha512-3Z8KZ6Vnc2x6rr8sNJ4QNYpkAttLBfb91uPzDlFDY7vgJg+vfXT8YWyZznVL+19ZixF2C/F4Ucp4/YjG2e1drg==} + hasBin: true '@turbo/workspaces@1.12.4': resolution: {integrity: sha512-a1hF8Nr6MOeCpvlLR569dGTlzgRLj2Rxo6dTb4jtL+jhHwCb94A9kDPgcRnYGFr45mgulICarVaNZxDjw4/riQ==} + hasBin: true '@types/acorn@4.0.6': resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} @@ -1783,6 +2096,7 @@ packages: acorn@8.10.0: resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} engines: {node: '>=0.4.0'} + hasBin: true acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} @@ -2468,10 +2782,105 @@ packages: dot-case@2.1.1: resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==} + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + drizzle-kit@0.22.8: + resolution: {integrity: sha512-VjI4wsJjk3hSqHSa3TwBf+uvH6M6pRHyxyoVbt935GUzP9tUR/BRZ+MhEJNgryqbzN2Za1KP0eJMTgKEPsalYQ==} + hasBin: true + + drizzle-orm@0.31.2: + resolution: {integrity: sha512-QnenevbnnAzmbNzQwbhklvIYrDE8YER8K7kSrAWQSV1YvFCdSQPzj+jzqRdTSsV2cDqSpQ0NXGyL1G9I43LDLg==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=3' + '@electric-sql/pglite': '>=0.1.1' + '@libsql/client': '*' + '@neondatabase/serverless': '>=0.1' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/react': '>=18' + '@types/sql.js': '*' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=13.2.0' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + react: '>=18' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/react': + optional: true + '@types/sql.js': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + react: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + duplexify@3.7.1: resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} @@ -2550,11 +2959,26 @@ packages: peerDependencies: esbuild: ^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 + esbuild-register@3.5.0: + resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==} + peerDependencies: + esbuild: '>=0.12 <1' + esbuild@0.17.6: resolution: {integrity: sha512-TKFRp9TxrJDdRWfSsSERKEovm6v30iHnrjlcGhLBOtReE28Yp1VSBRfO3GTaOFMoxsNerx4TjrhzSuma9ha83Q==} engines: {node: '>=12'} hasBin: true + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -2586,6 +3010,7 @@ packages: escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} + hasBin: true eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} @@ -2695,6 +3120,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} @@ -3039,6 +3465,7 @@ packages: handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} + hasBin: true has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -3559,10 +3986,12 @@ packages: js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} + hasBin: true jsesc@3.0.2: resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} @@ -3958,6 +4387,7 @@ packages: mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} @@ -4375,6 +4805,10 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + postgres@3.4.4: + resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==} + engines: {node: '>=12'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4391,6 +4825,7 @@ packages: prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} + hasBin: true pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} @@ -4480,6 +4915,7 @@ packages: rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} @@ -4608,6 +5044,7 @@ packages: resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} @@ -5202,6 +5639,7 @@ packages: typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} + hasBin: true ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} @@ -5209,6 +5647,7 @@ packages: uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} + hasBin: true uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} @@ -5427,6 +5866,7 @@ packages: which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} + hasBin: true which@3.0.1: resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} @@ -6029,138 +6469,283 @@ snapshots: '@emotion/hash@0.9.1': {} + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.7.2 + + '@esbuild/aix-ppc64@0.19.12': + optional: true + '@esbuild/aix-ppc64@0.20.2': optional: true '@esbuild/android-arm64@0.17.6': optional: true + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + '@esbuild/android-arm64@0.20.2': optional: true '@esbuild/android-arm@0.17.6': optional: true + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.19.12': + optional: true + '@esbuild/android-arm@0.20.2': optional: true '@esbuild/android-x64@0.17.6': optional: true + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + '@esbuild/android-x64@0.20.2': optional: true '@esbuild/darwin-arm64@0.17.6': optional: true + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + '@esbuild/darwin-arm64@0.20.2': optional: true '@esbuild/darwin-x64@0.17.6': optional: true + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + '@esbuild/darwin-x64@0.20.2': optional: true '@esbuild/freebsd-arm64@0.17.6': optional: true + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + '@esbuild/freebsd-arm64@0.20.2': optional: true '@esbuild/freebsd-x64@0.17.6': optional: true + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + '@esbuild/freebsd-x64@0.20.2': optional: true '@esbuild/linux-arm64@0.17.6': optional: true + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + '@esbuild/linux-arm64@0.20.2': optional: true '@esbuild/linux-arm@0.17.6': optional: true + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + '@esbuild/linux-arm@0.20.2': optional: true '@esbuild/linux-ia32@0.17.6': optional: true + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + '@esbuild/linux-ia32@0.20.2': optional: true '@esbuild/linux-loong64@0.17.6': optional: true + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.19.12': + optional: true + '@esbuild/linux-loong64@0.20.2': optional: true '@esbuild/linux-mips64el@0.17.6': optional: true + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + '@esbuild/linux-mips64el@0.20.2': optional: true '@esbuild/linux-ppc64@0.17.6': optional: true + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + '@esbuild/linux-ppc64@0.20.2': optional: true '@esbuild/linux-riscv64@0.17.6': optional: true + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + '@esbuild/linux-riscv64@0.20.2': optional: true '@esbuild/linux-s390x@0.17.6': optional: true + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + '@esbuild/linux-s390x@0.20.2': optional: true '@esbuild/linux-x64@0.17.6': optional: true + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + '@esbuild/linux-x64@0.20.2': optional: true '@esbuild/netbsd-x64@0.17.6': optional: true + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + '@esbuild/netbsd-x64@0.20.2': optional: true '@esbuild/openbsd-x64@0.17.6': optional: true + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + '@esbuild/openbsd-x64@0.20.2': optional: true '@esbuild/sunos-x64@0.17.6': optional: true + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + '@esbuild/sunos-x64@0.20.2': optional: true '@esbuild/win32-arm64@0.17.6': optional: true + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + '@esbuild/win32-arm64@0.20.2': optional: true '@esbuild/win32-ia32@0.17.6': optional: true + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + '@esbuild/win32-ia32@0.20.2': optional: true '@esbuild/win32-x64@0.17.6': optional: true + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + '@esbuild/win32-x64@0.20.2': optional: true @@ -6422,6 +7007,13 @@ snapshots: '@jspm/core@2.0.1': {} + '@knaadh/nestjs-drizzle-postgres@1.0.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(drizzle-orm@0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1))(postgres@3.4.4)': + dependencies: + '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + drizzle-orm: 0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1) + postgres: 3.4.4 + tslib: 2.6.2 + '@ljharb/through@2.3.13': dependencies: call-bind: 1.0.7 @@ -6450,7 +7042,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/cli@10.3.2': + '@nestjs/cli@10.3.2(esbuild@0.19.12)': dependencies: '@angular-devkit/core': 17.1.2(chokidar@3.6.0) '@angular-devkit/schematics': 17.1.2(chokidar@3.6.0) @@ -6460,7 +7052,7 @@ snapshots: chokidar: 3.6.0 cli-table3: 0.6.3 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.90.1) + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.90.1(esbuild@0.19.12)) glob: 10.3.10 inquirer: 8.2.6 node-emoji: 1.11.0 @@ -6472,7 +7064,7 @@ snapshots: tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.1.0 typescript: 5.3.3 - webpack: 5.90.1 + webpack: 5.90.1(esbuild@0.19.12) webpack-node-externals: 3.0.0 transitivePeerDependencies: - esbuild @@ -6487,6 +7079,14 @@ snapshots: tslib: 2.6.2 uid: 2.0.2 + '@nestjs/config@3.2.3(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(rxjs@7.8.1)': + dependencies: + '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + dotenv: 16.4.5 + dotenv-expand: 10.0.0 + lodash: 4.17.21 + rxjs: 7.8.1 + '@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -6805,6 +7405,8 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@sinclair/typebox@0.32.34': {} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -8110,8 +8712,26 @@ snapshots: dependencies: no-case: 2.3.2 + dotenv-expand@10.0.0: {} + dotenv@16.0.3: {} + dotenv@16.4.5: {} + + drizzle-kit@0.22.8: + dependencies: + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.19.12 + esbuild-register: 3.5.0(esbuild@0.19.12) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1): + optionalDependencies: + '@types/react': 18.2.61 + postgres: 3.4.4 + react: 18.3.1 + duplexify@3.7.1: dependencies: end-of-stream: 1.4.4 @@ -8243,6 +8863,13 @@ snapshots: local-pkg: 0.5.0 resolve.exports: 2.0.2 + esbuild-register@3.5.0(esbuild@0.19.12): + dependencies: + debug: 4.3.4 + esbuild: 0.19.12 + transitivePeerDependencies: + - supports-color + esbuild@0.17.6: optionalDependencies: '@esbuild/android-arm': 0.17.6 @@ -8268,6 +8895,57 @@ snapshots: '@esbuild/win32-ia32': 0.17.6 '@esbuild/win32-x64': 0.17.6 + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + esbuild@0.19.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -8721,7 +9399,7 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.90.1): + fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.90.1(esbuild@0.19.12)): dependencies: '@babel/code-frame': 7.22.13 chalk: 4.1.2 @@ -8736,7 +9414,7 @@ snapshots: semver: 7.5.4 tapable: 2.2.1 typescript: 5.3.3 - webpack: 5.90.1 + webpack: 5.90.1(esbuild@0.19.12) form-data@4.0.0: dependencies: @@ -10666,6 +11344,8 @@ snapshots: picocolors: 1.0.0 source-map-js: 1.2.0 + postgres@3.4.4: {} + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.0: @@ -11389,14 +12069,16 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - terser-webpack-plugin@5.3.10(webpack@5.90.1): + terser-webpack-plugin@5.3.10(esbuild@0.19.12)(webpack@5.90.1(esbuild@0.19.12)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.0 - webpack: 5.90.1 + webpack: 5.90.1(esbuild@0.19.12) + optionalDependencies: + esbuild: 0.19.12 terser@5.31.0: dependencies: @@ -11470,7 +12152,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.1.2(@babel/core@7.23.3)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.3))(jest@29.7.0(@types/node@20.11.24)(ts-node@10.9.1(@types/node@20.11.24)(typescript@5.3.3)))(typescript@5.3.3): + ts-jest@29.1.2(@babel/core@7.23.3)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.3))(esbuild@0.19.12)(jest@29.7.0(@types/node@20.11.24)(ts-node@10.9.1(@types/node@20.11.24)(typescript@5.3.3)))(typescript@5.3.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -11486,8 +12168,9 @@ snapshots: '@babel/core': 7.23.3 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.23.3) + esbuild: 0.19.12 - ts-loader@9.5.1(typescript@5.3.3)(webpack@5.90.1): + ts-loader@9.5.1(typescript@5.3.3)(webpack@5.90.1(esbuild@0.19.12)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.15.0 @@ -11495,7 +12178,7 @@ snapshots: semver: 7.5.4 source-map: 0.7.4 typescript: 5.3.3 - webpack: 5.90.1 + webpack: 5.90.1(esbuild@0.19.12) ts-node@10.9.1(@types/node@20.11.24)(typescript@5.3.3): dependencies: @@ -11837,7 +12520,7 @@ snapshots: webpack-sources@3.2.3: {} - webpack@5.90.1: + webpack@5.90.1(esbuild@0.19.12): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.5 @@ -11860,7 +12543,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.90.1) + terser-webpack-plugin: 5.3.10(esbuild@0.19.12)(webpack@5.90.1(esbuild@0.19.12)) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: From e08cbe23b994e3b9ea7e89d16f203944678b3bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stas=CC=81kiewicz?= Date: Fri, 5 Jul 2024 14:38:23 +0200 Subject: [PATCH 2/8] chore: add drizzle scripts --- .../common_nestjs_remix/apps/api/package.json | 4 +- .../storage/migrations/0000_create_users.sql | 5 ++ .../migrations/meta/0000_snapshot.json | 46 +++++++++++++++++++ .../src/storage/migrations/meta/_journal.json | 13 ++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 examples/common_nestjs_remix/apps/api/src/storage/migrations/0000_create_users.sql create mode 100644 examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/0000_snapshot.json create mode 100644 examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/_journal.json diff --git a/examples/common_nestjs_remix/apps/api/package.json b/examples/common_nestjs_remix/apps/api/package.json index 8cd3728..fcb9f26 100644 --- a/examples/common_nestjs_remix/apps/api/package.json +++ b/examples/common_nestjs_remix/apps/api/package.json @@ -17,7 +17,9 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "jest --config ./test/jest-e2e.json", + "db:migrate": "drizzle-kit migrate", + "db:generate": "drizzle-kit generate" }, "dependencies": { "@knaadh/nestjs-drizzle-postgres": "^1.0.0", diff --git a/examples/common_nestjs_remix/apps/api/src/storage/migrations/0000_create_users.sql b/examples/common_nestjs_remix/apps/api/src/storage/migrations/0000_create_users.sql new file mode 100644 index 0000000..2d6dc5b --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/storage/migrations/0000_create_users.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS "users" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL +); diff --git a/examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/0000_snapshot.json b/examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..dc9c6ad --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/0000_snapshot.json @@ -0,0 +1,46 @@ +{ + "id": "e69dc2c4-c21a-4d2a-bceb-820005722d28", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/_journal.json b/examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/_journal.json new file mode 100644 index 0000000..c6a785c --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/storage/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1720182483074, + "tag": "0000_create_users", + "breakpoints": true + } + ] +} \ No newline at end of file From c46a759022123d1e8dfa5dfc0ab5d567b20aed0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stas=CC=81kiewicz?= Date: Fri, 5 Jul 2024 16:37:14 +0200 Subject: [PATCH 3/8] feat: add swagger --- .../common_nestjs_remix/apps/api/package.json | 4 + .../apps/api/src/app.module.ts | 2 + .../apps/api/src/common/index.ts | 4 + .../common_nestjs_remix/apps/api/src/main.ts | 23 +++- .../api/src/management/management.module.ts | 13 ++ .../management/use-cases/create-property.ts | 51 ++++++++ .../apps/api/src/storage/schema/index.ts | 10 +- examples/common_nestjs_remix/pnpm-lock.yaml | 121 +++++++++++++++++- 8 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 examples/common_nestjs_remix/apps/api/src/common/index.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/management/management.module.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts diff --git a/examples/common_nestjs_remix/apps/api/package.json b/examples/common_nestjs_remix/apps/api/package.json index fcb9f26..2809f93 100644 --- a/examples/common_nestjs_remix/apps/api/package.json +++ b/examples/common_nestjs_remix/apps/api/package.json @@ -26,10 +26,14 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", + "@nestjs/cqrs": "^10.2.7", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.4.0", "@sinclair/typebox": "^0.32.34", "drizzle-kit": "^0.22.8", "drizzle-orm": "^0.31.2", + "drizzle-typebox": "^0.1.1", + "nestjs-typebox": "3.0.0-next.8", "postgres": "^3.4.4", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" diff --git a/examples/common_nestjs_remix/apps/api/src/app.module.ts b/examples/common_nestjs_remix/apps/api/src/app.module.ts index 9743d10..725422b 100644 --- a/examples/common_nestjs_remix/apps/api/src/app.module.ts +++ b/examples/common_nestjs_remix/apps/api/src/app.module.ts @@ -5,6 +5,7 @@ import { DrizzlePostgresModule } from "@knaadh/nestjs-drizzle-postgres"; import database from "./common/configuration/database"; import { ConfigModule, ConfigService } from "@nestjs/config"; import * as schema from "./storage/schema"; +import { ManagementModule } from "./management/management.module"; @Module({ imports: [ @@ -26,6 +27,7 @@ import * as schema from "./storage/schema"; }, inject: [ConfigService], }), + ManagementModule, ], controllers: [AppController], providers: [AppService], diff --git a/examples/common_nestjs_remix/apps/api/src/common/index.ts b/examples/common_nestjs_remix/apps/api/src/common/index.ts new file mode 100644 index 0000000..bef7eb9 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/common/index.ts @@ -0,0 +1,4 @@ +import { PostgresJsDatabase } from "drizzle-orm/postgres-js"; +import * as schema from "src/storage/schema"; + +export type DatabasePg = PostgresJsDatabase; diff --git a/examples/common_nestjs_remix/apps/api/src/main.ts b/examples/common_nestjs_remix/apps/api/src/main.ts index 13cad38..cb44f46 100644 --- a/examples/common_nestjs_remix/apps/api/src/main.ts +++ b/examples/common_nestjs_remix/apps/api/src/main.ts @@ -1,8 +1,27 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { NestFactory } from "@nestjs/core"; +import { AppModule } from "./app.module"; +import { + patchNestJsSwagger, + applyFormats, + // TypeboxValidationPipe, + // TypeboxTransformInterceptor, +} from "nestjs-typebox"; +import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; + +patchNestJsSwagger(); +applyFormats(); async function bootstrap() { const app = await NestFactory.create(AppModule); + + const config = new DocumentBuilder() + .setTitle("Guidebook API") + .setDescription("Example usage of Swagger with Typebox") + .setVersion("1.0") + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup("api", app, document); + await app.listen(3000); } bootstrap(); diff --git a/examples/common_nestjs_remix/apps/api/src/management/management.module.ts b/examples/common_nestjs_remix/apps/api/src/management/management.module.ts new file mode 100644 index 0000000..bd4e838 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/management/management.module.ts @@ -0,0 +1,13 @@ +import { Module } from "@nestjs/common"; +import { CqrsModule } from "@nestjs/cqrs"; +import { + CreatePropertyController, + CreatePropertyHandler, +} from "./use-cases/create-property"; + +@Module({ + imports: [CqrsModule], + controllers: [CreatePropertyController], + providers: [CreatePropertyHandler], +}) +export class ManagementModule {} diff --git a/examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts b/examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts new file mode 100644 index 0000000..974f533 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts @@ -0,0 +1,51 @@ +import { Controller, Inject, Post } from "@nestjs/common"; +import { CommandBus, CommandHandler, ICommandHandler } from "@nestjs/cqrs"; +import { type Static, Type } from "@sinclair/typebox"; +import { createSelectSchema } from "drizzle-typebox"; +import { Validate } from "nestjs-typebox"; +import { DatabasePg } from "src/common"; +import { properties } from "src/storage/schema"; + +export class CreateProperty { + name: string; + description?: string; + + constructor(data: Partial) { + Object.assign(this, data); + } +} + +const requestSchema = Type.Object({ + name: Type.String(), + description: Type.Optional(Type.String()), +}); +const responseSchema = createSelectSchema(properties); + +@Controller("properties") +export class CreatePropertyController { + constructor(private readonly commandBus: CommandBus) {} + + @Post() + @Validate({ + response: responseSchema, + request: [{ type: "body", schema: requestSchema }], + }) + async createProperty(data: Static) { + const command = new CreateProperty(data); + return this.commandBus.execute(command); + } +} + +@CommandHandler(CreateProperty) +export class CreatePropertyHandler implements ICommandHandler { + constructor(@Inject("DB") private readonly db: DatabasePg) {} + + async execute(command: CreateProperty) { + const [property] = await this.db + .insert(properties) + .values(command) + .returning(); + + return property; + } +} diff --git a/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts b/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts index 15287d9..08e6991 100644 --- a/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts +++ b/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts @@ -1,7 +1,15 @@ -import { pgTable } from "drizzle-orm/pg-core"; +import { boolean, pgTable, text } from "drizzle-orm/pg-core"; import { id, timestamps } from "./utils"; export const users = pgTable("users", { ...id, ...timestamps, }); + +export const properties = pgTable("properties", { + ...id, + ...timestamps, + + name: text("name").notNull(), + description: text("description"), +}); diff --git a/examples/common_nestjs_remix/pnpm-lock.yaml b/examples/common_nestjs_remix/pnpm-lock.yaml index 7515f15..f8de558 100644 --- a/examples/common_nestjs_remix/pnpm-lock.yaml +++ b/examples/common_nestjs_remix/pnpm-lock.yaml @@ -35,9 +35,15 @@ importers: '@nestjs/core': specifier: ^10.0.0 version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/cqrs': + specifier: ^10.2.7 + version: 10.2.7(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8) + '@nestjs/swagger': + specifier: ^7.4.0 + version: 7.4.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2) '@sinclair/typebox': specifier: ^0.32.34 version: 0.32.34 @@ -47,6 +53,12 @@ importers: drizzle-orm: specifier: ^0.31.2 version: 0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1) + drizzle-typebox: + specifier: ^0.1.1 + version: 0.1.1(@sinclair/typebox@0.32.34)(drizzle-orm@0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1)) + nestjs-typebox: + specifier: 3.0.0-next.8 + version: 3.0.0-next.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/swagger@7.4.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2))(@sinclair/typebox@0.32.34)(rxjs@7.8.1) postgres: specifier: ^3.4.4 version: 3.4.4 @@ -1403,6 +1415,9 @@ packages: '@mdx-js/mdx@2.3.0': resolution: {integrity: sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==} + '@microsoft/tsdoc@0.15.0': + resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@nestjs/cli@10.3.2': resolution: {integrity: sha512-aWmD1GLluWrbuC4a1Iz/XBk5p74Uj6nIVZj6Ov03JbTfgtWqGFLtXuMetvzMiHxfrHehx/myt2iKAPRhKdZvTg==} engines: {node: '>= 16.14'} @@ -1452,6 +1467,27 @@ packages: '@nestjs/websockets': optional: true + '@nestjs/cqrs@10.2.7': + resolution: {integrity: sha512-RXhgQOfuT+KzvkueR4S++SB6+6333PL71pOtCzbJAAU/DY3KY56yTCncWRsIdorKfDX5AEwTiQHHJi69XJWdkA==} + peerDependencies: + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 || ^0.2.0 + rxjs: ^7.2.0 + + '@nestjs/mapped-types@2.0.5': + resolution: {integrity: sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + '@nestjs/platform-express@10.3.8': resolution: {integrity: sha512-sifLoxgEJvAgbim1UuW6wyScMfkS9SVQRH+lN33N/9ZvZSjO6NSDLOe+wxqsnZkia+QrjFC0qy0ITRAsggfqbg==} peerDependencies: @@ -1463,6 +1499,23 @@ packages: peerDependencies: typescript: '>=4.8.2' + '@nestjs/swagger@7.4.0': + resolution: {integrity: sha512-dCiwKkRxcR7dZs5jtrGspBAe/nqJd1AYzOBTzw9iCdbq3BGrLpwokelk6lFZPe4twpTsPQqzNKBwKzVbI6AR/g==} + peerDependencies: + '@fastify/static': ^6.0.0 || ^7.0.0 + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + class-transformer: + optional: true + class-validator: + optional: true + '@nestjs/testing@10.3.8': resolution: {integrity: sha512-hpX9das2TdFTKQ4/2ojhjI6YgXtCfXRKui3A4Qaj54VVzc5+mtK502Jj18Vzji98o9MVS6skmYu+S/UvW3U6Fw==} peerDependencies: @@ -2881,6 +2934,12 @@ packages: sqlite3: optional: true + drizzle-typebox@0.1.1: + resolution: {integrity: sha512-eNIDe+EOCB96/bbRHOPbrC+bsuCXFd2H0/96Fl0cJkY+NKnu2qLTnGI6ILXPbyu82Yr1tsShlzDypw+VeCDqaQ==} + peerDependencies: + '@sinclair/typebox': '>=0.17.6' + drizzle-orm: '>=0.23.13' + duplexify@3.7.1: resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} @@ -4450,6 +4509,15 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + nestjs-typebox@3.0.0-next.8: + resolution: {integrity: sha512-iOOgf3jq23MrG87YU5/LLj92wji9FhW8p7PPKooqI2unVnJDMImNkRd74eHwmGs03GT7QBEp2iQkuG+TRtcpyg==} + peerDependencies: + '@nestjs/common': ^9.0.1 || ^10.0.3 + '@nestjs/core': ^9.0.1 || ^10.0.3 + '@nestjs/swagger': ^6.1.1 || ^7.0.11 + '@sinclair/typebox': ^0.32.4 + rxjs: ^7.5.6 + netmask@2.0.2: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} @@ -5367,6 +5435,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + swagger-ui-dist@5.17.14: + resolution: {integrity: sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==} + swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} @@ -5738,6 +5809,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} @@ -7042,6 +7117,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@microsoft/tsdoc@0.15.0': {} + '@nestjs/cli@10.3.2(esbuild@0.19.12)': dependencies: '@angular-devkit/core': 17.1.2(chokidar@3.6.0) @@ -7103,6 +7180,19 @@ snapshots: transitivePeerDependencies: - encoding + '@nestjs/cqrs@10.2.7(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': + dependencies: + '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1) + reflect-metadata: 0.2.2 + rxjs: 7.8.1 + uuid: 9.0.1 + + '@nestjs/mapped-types@2.0.5(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)': + dependencies: + '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + reflect-metadata: 0.2.2 + '@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)': dependencies: '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -7126,6 +7216,18 @@ snapshots: transitivePeerDependencies: - chokidar + '@nestjs/swagger@7.4.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)': + dependencies: + '@microsoft/tsdoc': 0.15.0 + '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2) + js-yaml: 4.1.0 + lodash: 4.17.21 + path-to-regexp: 3.2.0 + reflect-metadata: 0.2.2 + swagger-ui-dist: 5.17.14 + '@nestjs/testing@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8))': dependencies: '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -7864,7 +7966,7 @@ snapshots: '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.5) '@vanilla-extract/babel-plugin-debug-ids': 1.0.6 '@vanilla-extract/css': 1.15.2 - esbuild: 0.17.6 + esbuild: 0.19.12 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 @@ -8732,6 +8834,11 @@ snapshots: postgres: 3.4.4 react: 18.3.1 + drizzle-typebox@0.1.1(@sinclair/typebox@0.32.34)(drizzle-orm@0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1)): + dependencies: + '@sinclair/typebox': 0.32.34 + drizzle-orm: 0.31.2(@types/react@18.2.61)(postgres@3.4.4)(react@18.3.1) + duplexify@3.7.1: dependencies: end-of-stream: 1.4.4 @@ -10958,6 +11065,14 @@ snapshots: neo-async@2.6.2: {} + nestjs-typebox@3.0.0-next.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/swagger@7.4.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2))(@sinclair/typebox@0.32.34)(rxjs@7.8.1): + dependencies: + '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/swagger': 7.4.0(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2) + '@sinclair/typebox': 0.32.34 + rxjs: 7.8.1 + netmask@2.0.2: {} no-case@2.3.2: @@ -12004,6 +12119,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + swagger-ui-dist@5.17.14: {} + swap-case@1.1.2: dependencies: lower-case: 1.1.4 @@ -12417,6 +12534,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@9.0.1: {} + uvu@0.5.6: dependencies: dequal: 2.0.3 From 3c6738e558bfb6c303231caad82933628b1c283d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stas=CC=81kiewicz?= Date: Fri, 5 Jul 2024 16:44:35 +0200 Subject: [PATCH 4/8] chore: output swagger json --- .../apps/api/src/app.controller.spec.ts | 22 ----- .../apps/api/src/app.controller.ts | 12 --- .../apps/api/src/app.module.ts | 6 +- .../apps/api/src/app.service.ts | 8 -- .../common_nestjs_remix/apps/api/src/main.ts | 9 +- .../apps/api/src/swagger/api-schema.json | 92 +++++++++++++++++++ .../api/src/utils/save-swagger-to-file.ts | 13 +++ 7 files changed, 110 insertions(+), 52 deletions(-) delete mode 100644 examples/common_nestjs_remix/apps/api/src/app.controller.spec.ts delete mode 100644 examples/common_nestjs_remix/apps/api/src/app.controller.ts delete mode 100644 examples/common_nestjs_remix/apps/api/src/app.service.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json create mode 100644 examples/common_nestjs_remix/apps/api/src/utils/save-swagger-to-file.ts diff --git a/examples/common_nestjs_remix/apps/api/src/app.controller.spec.ts b/examples/common_nestjs_remix/apps/api/src/app.controller.spec.ts deleted file mode 100644 index d22f389..0000000 --- a/examples/common_nestjs_remix/apps/api/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); -}); diff --git a/examples/common_nestjs_remix/apps/api/src/app.controller.ts b/examples/common_nestjs_remix/apps/api/src/app.controller.ts deleted file mode 100644 index cce879e..0000000 --- a/examples/common_nestjs_remix/apps/api/src/app.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} diff --git a/examples/common_nestjs_remix/apps/api/src/app.module.ts b/examples/common_nestjs_remix/apps/api/src/app.module.ts index 725422b..32b24ee 100644 --- a/examples/common_nestjs_remix/apps/api/src/app.module.ts +++ b/examples/common_nestjs_remix/apps/api/src/app.module.ts @@ -1,6 +1,4 @@ import { Module } from "@nestjs/common"; -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; import { DrizzlePostgresModule } from "@knaadh/nestjs-drizzle-postgres"; import database from "./common/configuration/database"; import { ConfigModule, ConfigService } from "@nestjs/config"; @@ -29,7 +27,7 @@ import { ManagementModule } from "./management/management.module"; }), ManagementModule, ], - controllers: [AppController], - providers: [AppService], + controllers: [], + providers: [], }) export class AppModule {} diff --git a/examples/common_nestjs_remix/apps/api/src/app.service.ts b/examples/common_nestjs_remix/apps/api/src/app.service.ts deleted file mode 100644 index 927d7cc..0000000 --- a/examples/common_nestjs_remix/apps/api/src/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AppService { - getHello(): string { - return 'Hello World!'; - } -} diff --git a/examples/common_nestjs_remix/apps/api/src/main.ts b/examples/common_nestjs_remix/apps/api/src/main.ts index cb44f46..75b7d8e 100644 --- a/examples/common_nestjs_remix/apps/api/src/main.ts +++ b/examples/common_nestjs_remix/apps/api/src/main.ts @@ -1,12 +1,8 @@ import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; -import { - patchNestJsSwagger, - applyFormats, - // TypeboxValidationPipe, - // TypeboxTransformInterceptor, -} from "nestjs-typebox"; +import { patchNestJsSwagger, applyFormats } from "nestjs-typebox"; import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; +import { exportSchemaToFile } from "./utils/save-swagger-to-file"; patchNestJsSwagger(); applyFormats(); @@ -21,6 +17,7 @@ async function bootstrap() { .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup("api", app, document); + exportSchemaToFile(document); await app.listen(3000); } diff --git a/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json b/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json new file mode 100644 index 0000000..3ed3930 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json @@ -0,0 +1,92 @@ +{ + "openapi": "3.0.0", + "paths": { + "/properties": { + "post": { + "operationId": "CreatePropertyController_createProperty", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePropertyBody" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePropertyResponse" + } + } + } + } + } + } + } + }, + "info": { + "title": "Guidebook API", + "description": "Example usage of Swagger with Typebox", + "version": "1.0", + "contact": {} + }, + "tags": [], + "servers": [], + "components": { + "schemas": { + "CreatePropertyBody": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "CreatePropertyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "createdAt", + "updatedAt", + "name", + "description" + ] + } + } + } +} \ No newline at end of file diff --git a/examples/common_nestjs_remix/apps/api/src/utils/save-swagger-to-file.ts b/examples/common_nestjs_remix/apps/api/src/utils/save-swagger-to-file.ts new file mode 100644 index 0000000..2317443 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/utils/save-swagger-to-file.ts @@ -0,0 +1,13 @@ +import { writeFile } from 'node:fs'; + +const SCHEMA_FILE = './src/swagger/api-schema.json'; + +export const exportSchemaToFile = (schema: object) => { + const content = JSON.stringify(schema, null, 2); + + writeFile(SCHEMA_FILE, content, (err) => { + if (err) { + return console.error(err); + } + }); +}; From a0cab6396faa273ff8902b4296c1eaee3e70f86d Mon Sep 17 00:00:00 2001 From: Tomasz Kielar Date: Fri, 5 Jul 2024 17:33:39 +0200 Subject: [PATCH 5/8] feat: adjust frontend for api generation --- .../common_nestjs_remix/apps/web/.env.example | 1 + .../apps/web/app/api/api-client.ts | 6 + .../apps/web/app/api/generated-api.ts | 184 ++++++++++++++++++ .../app/api/mutations/useCreateProperty.ts | 21 ++ .../common_nestjs_remix/apps/web/package.json | 3 +- .../apps/web/vite.config.ts | 2 +- examples/common_nestjs_remix/package.json | 5 +- examples/common_nestjs_remix/pnpm-lock.yaml | 25 +++ 8 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 examples/common_nestjs_remix/apps/web/.env.example create mode 100644 examples/common_nestjs_remix/apps/web/app/api/api-client.ts create mode 100644 examples/common_nestjs_remix/apps/web/app/api/generated-api.ts create mode 100644 examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts diff --git a/examples/common_nestjs_remix/apps/web/.env.example b/examples/common_nestjs_remix/apps/web/.env.example new file mode 100644 index 0000000..d16cbef --- /dev/null +++ b/examples/common_nestjs_remix/apps/web/.env.example @@ -0,0 +1 @@ +VITE_API_URL='' diff --git a/examples/common_nestjs_remix/apps/web/app/api/api-client.ts b/examples/common_nestjs_remix/apps/web/app/api/api-client.ts new file mode 100644 index 0000000..0ff5c36 --- /dev/null +++ b/examples/common_nestjs_remix/apps/web/app/api/api-client.ts @@ -0,0 +1,6 @@ +import { API } from "./generated-api"; + +export const ApiClient = new API({ + baseURL: import.meta.env.API_URL, + secure: true, +}); diff --git a/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts b/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts new file mode 100644 index 0000000..16e9ac1 --- /dev/null +++ b/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts @@ -0,0 +1,184 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export interface CreatePropertyBody { + name: string; + description?: string; +} + +export interface CreatePropertyResponse { + id: string; + createdAt: string; + updatedAt: string; + name: string; + description: string | null; +} + +import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from "axios"; +import axios from "axios"; + +export type QueryParamsType = Record; + +export interface FullRequestParams extends Omit { + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseType; + /** request body */ + body?: unknown; +} + +export type RequestParams = Omit; + +export interface ApiConfig extends Omit { + securityWorker?: ( + securityData: SecurityDataType | null, + ) => Promise | AxiosRequestConfig | void; + secure?: boolean; + format?: ResponseType; +} + +export enum ContentType { + Json = "application/json", + FormData = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded", + Text = "text/plain", +} + +export class HttpClient { + public instance: AxiosInstance; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig["securityWorker"]; + private secure?: boolean; + private format?: ResponseType; + + constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig = {}) { + this.instance = axios.create({ ...axiosConfig, baseURL: axiosConfig.baseURL || "" }); + this.secure = secure; + this.format = format; + this.securityWorker = securityWorker; + } + + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; + + protected mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig { + const method = params1.method || (params2 && params2.method); + + return { + ...this.instance.defaults, + ...params1, + ...(params2 || {}), + headers: { + ...((method && this.instance.defaults.headers[method.toLowerCase() as keyof HeadersDefaults]) || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected stringifyFormItem(formItem: unknown) { + if (typeof formItem === "object" && formItem !== null) { + return JSON.stringify(formItem); + } else { + return `${formItem}`; + } + } + + protected createFormData(input: Record): FormData { + if (input instanceof FormData) { + return input; + } + return Object.keys(input || {}).reduce((formData, key) => { + const property = input[key]; + const propertyContent: any[] = property instanceof Array ? property : [property]; + + for (const formItem of propertyContent) { + const isFileType = formItem instanceof Blob || formItem instanceof File; + formData.append(key, isFileType ? formItem : this.stringifyFormItem(formItem)); + } + + return formData; + }, new FormData()); + } + + public request = async ({ + secure, + path, + type, + query, + format, + body, + ...params + }: FullRequestParams): Promise> => { + const secureParams = + ((typeof secure === "boolean" ? secure : this.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const responseFormat = format || this.format || undefined; + + if (type === ContentType.FormData && body && body !== null && typeof body === "object") { + body = this.createFormData(body as Record); + } + + if (type === ContentType.Text && body && body !== null && typeof body !== "string") { + body = JSON.stringify(body); + } + + return this.instance.request({ + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), + }, + params: query, + responseType: responseFormat, + data: body, + url: path, + }); + }; +} + +/** + * @title Guidebook API + * @version 1.0 + * @contact + * + * Example usage of Swagger with Typebox + */ +export class API extends HttpClient { + properties = { + /** + * No description + * + * @name CreatePropertyControllerCreateProperty + * @request POST:/properties + */ + createPropertyControllerCreateProperty: (data: CreatePropertyBody, params: RequestParams = {}) => + this.request({ + path: `/properties`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }), + }; +} diff --git a/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts b/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts new file mode 100644 index 0000000..826a297 --- /dev/null +++ b/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts @@ -0,0 +1,21 @@ +import { useMutation } from "@tanstack/react-query"; +import { CreatePropertyBody } from "../generated-api"; +import { ApiClient } from "../api-client"; + +type CreatePropertyOptions = { + data: CreatePropertyBody; + // id: string +}; + +export function useCreateProperty() { + return useMutation({ + mutationFn: async (options: CreatePropertyOptions) => { + const response = + await ApiClient.properties.createPropertyControllerCreateProperty( + options.data + ); + + return response.data; + }, + }); +} diff --git a/examples/common_nestjs_remix/apps/web/package.json b/examples/common_nestjs_remix/apps/web/package.json index 312424d..d2b6476 100644 --- a/examples/common_nestjs_remix/apps/web/package.json +++ b/examples/common_nestjs_remix/apps/web/package.json @@ -9,7 +9,7 @@ "dev_ssr": "node ./server.js", "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", "start": "cross-env NODE_ENV=production node ./server.js", - "generate:client": "swagger-typescript-api -p ../api/src/swagger/api-schema.json -o ./src/api --axios --name generated-api.ts --api-class-name API", + "generate:client": "swagger-typescript-api -p ../api/src/swagger/api-schema.json -o ./app/api --axios --name generated-api.ts --api-class-name API", "typecheck": "tsc" }, "dependencies": { @@ -17,6 +17,7 @@ "@remix-run/node": "^2.9.2", "@remix-run/react": "^2.9.2", "@tanstack/react-query": "^5.40.1", + "axios": "^1.7.2", "compression": "^1.7.4", "express": "^4.18.2", "isbot": "^4.1.0", diff --git a/examples/common_nestjs_remix/apps/web/vite.config.ts b/examples/common_nestjs_remix/apps/web/vite.config.ts index 98fdeb2..2588e46 100644 --- a/examples/common_nestjs_remix/apps/web/vite.config.ts +++ b/examples/common_nestjs_remix/apps/web/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ v3_throwAbortReason: true, unstable_singleFetch: true, }, - ssr: false, + ssr: false, // SPA MODE - Might migrate to React Router 7 routes, }), tsconfigPaths(), diff --git a/examples/common_nestjs_remix/package.json b/examples/common_nestjs_remix/package.json index eb1be59..3e7d622 100644 --- a/examples/common_nestjs_remix/package.json +++ b/examples/common_nestjs_remix/package.json @@ -5,7 +5,10 @@ "build": "turbo build", "dev": "turbo dev", "lint": "turbo lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"" + "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "generate:client": "pnpm run --filter=web generate:client", + "db:generate": "pnpm run --filter=api db:generate", + "db:migrate": "pnpm run --filter=api db:migrate" }, "devDependencies": { "@repo/eslint-config": "workspace:*", diff --git a/examples/common_nestjs_remix/pnpm-lock.yaml b/examples/common_nestjs_remix/pnpm-lock.yaml index 0584a52..67162dc 100644 --- a/examples/common_nestjs_remix/pnpm-lock.yaml +++ b/examples/common_nestjs_remix/pnpm-lock.yaml @@ -149,6 +149,9 @@ importers: '@tanstack/react-query': specifier: ^5.40.1 version: 5.50.1(react@18.3.1) + axios: + specifier: ^1.7.2 + version: 1.7.2 compression: specifier: ^1.7.4 version: 1.7.4 @@ -2159,6 +2162,9 @@ packages: resolution: {integrity: sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==} engines: {node: '>=4'} + axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + axobject-query@3.1.1: resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==} @@ -3209,6 +3215,15 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -8047,6 +8062,14 @@ snapshots: axe-core@4.9.1: {} + axios@1.7.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axobject-query@3.1.1: dependencies: deep-equal: 2.2.3 @@ -9328,6 +9351,8 @@ snapshots: flatted@3.3.1: {} + follow-redirects@1.15.6: {} + for-each@0.3.3: dependencies: is-callable: 1.2.7 From 2de7aad80c911395d90cec0bf49a87d9e4ab3e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stas=CC=81kiewicz?= Date: Mon, 8 Jul 2024 11:38:39 +0200 Subject: [PATCH 6/8] chore: move to simplified structure --- .../apps/api/src/common/index.ts | 21 +++++ .../management/api/properties.controller.ts | 40 +++++++++ .../api/src/management/management.module.ts | 14 ++-- .../api/src/management/properties.service.ts | 31 +++++++ .../src/management/schemas/create-property.ts | 6 ++ .../api/src/management/schemas/property.ts | 4 + .../management/use-cases/create-property.ts | 51 ----------- .../apps/api/src/storage/schema/index.ts | 2 +- .../apps/api/src/swagger/api-schema.json | 84 ++++++++++++++----- 9 files changed, 170 insertions(+), 83 deletions(-) create mode 100644 examples/common_nestjs_remix/apps/api/src/management/api/properties.controller.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/management/properties.service.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/management/schemas/create-property.ts create mode 100644 examples/common_nestjs_remix/apps/api/src/management/schemas/property.ts delete mode 100644 examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts diff --git a/examples/common_nestjs_remix/apps/api/src/common/index.ts b/examples/common_nestjs_remix/apps/api/src/common/index.ts index bef7eb9..dea7819 100644 --- a/examples/common_nestjs_remix/apps/api/src/common/index.ts +++ b/examples/common_nestjs_remix/apps/api/src/common/index.ts @@ -1,4 +1,25 @@ +import { TObject, Type } from "@sinclair/typebox"; import { PostgresJsDatabase } from "drizzle-orm/postgres-js"; import * as schema from "src/storage/schema"; export type DatabasePg = PostgresJsDatabase; + +export class BaseResponse { + data: T; + + constructor(data: T) { + this.data = data; + } +} + +export const UUIDSchema = Type.String({ format: "uuid" }); + +export function baseResponse(data: TObject) { + return Type.Object({ + data, + }); +} + +export function nullResponse() { + return Type.Null(); +} diff --git a/examples/common_nestjs_remix/apps/api/src/management/api/properties.controller.ts b/examples/common_nestjs_remix/apps/api/src/management/api/properties.controller.ts new file mode 100644 index 0000000..2d7bd20 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/management/api/properties.controller.ts @@ -0,0 +1,40 @@ +import { Controller, Delete, Post } from "@nestjs/common"; +import { Validate } from "nestjs-typebox"; +import { propertySchema } from "../schemas/property"; +import { createPropertySchema } from "../schemas/create-property"; +import { Static } from "@sinclair/typebox"; +import { PropertiesService } from "../properties.service"; +import { + BaseResponse, + UUIDSchema, + baseResponse, + nullResponse, +} from "src/common"; + +@Controller("properties") +export class PropertiesController { + constructor(private readonly propertiesService: PropertiesService) {} + + @Post() + @Validate({ + response: baseResponse(propertySchema), + request: [{ type: "body", schema: createPropertySchema }], + }) + async createProperty( + data: Static, + ): Promise>> { + const property = await this.propertiesService.createProperty(data); + + return new BaseResponse(property); + } + + @Delete(":id") + @Validate({ + response: nullResponse(), + request: [{ name: "id", type: "param", schema: UUIDSchema }], + }) + async deleteProperty(id: string): Promise { + await this.propertiesService.deleteProperty(id); + return null; + } +} diff --git a/examples/common_nestjs_remix/apps/api/src/management/management.module.ts b/examples/common_nestjs_remix/apps/api/src/management/management.module.ts index bd4e838..843ca66 100644 --- a/examples/common_nestjs_remix/apps/api/src/management/management.module.ts +++ b/examples/common_nestjs_remix/apps/api/src/management/management.module.ts @@ -1,13 +1,11 @@ import { Module } from "@nestjs/common"; -import { CqrsModule } from "@nestjs/cqrs"; -import { - CreatePropertyController, - CreatePropertyHandler, -} from "./use-cases/create-property"; +import { PropertiesController } from "./api/properties.controller"; +import { PropertiesService } from "./properties.service"; @Module({ - imports: [CqrsModule], - controllers: [CreatePropertyController], - providers: [CreatePropertyHandler], + imports: [], + controllers: [PropertiesController], + providers: [PropertiesService], + exports: [], }) export class ManagementModule {} diff --git a/examples/common_nestjs_remix/apps/api/src/management/properties.service.ts b/examples/common_nestjs_remix/apps/api/src/management/properties.service.ts new file mode 100644 index 0000000..9d4bd66 --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/management/properties.service.ts @@ -0,0 +1,31 @@ +import { Inject, Injectable, NotFoundException } from "@nestjs/common"; +import { eq } from "drizzle-orm"; +import { DatabasePg } from "src/common"; +import { properties } from "src/storage/schema"; + +@Injectable() +export class PropertiesService { + constructor(@Inject("DB") private readonly db: DatabasePg) {} + + async createProperty(data: { name: string; description?: string }) { + const [property] = await this.db + .insert(properties) + .values({ + name: data.name, + description: data.description, + }) + .returning(); + + return property; + } + + async deleteProperty(id: string): Promise { + const [deleted] = await this.db + .delete(properties) + .where(eq(properties.id, id)); + + if (!deleted) { + throw new NotFoundException(); + } + } +} diff --git a/examples/common_nestjs_remix/apps/api/src/management/schemas/create-property.ts b/examples/common_nestjs_remix/apps/api/src/management/schemas/create-property.ts new file mode 100644 index 0000000..363dd2d --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/management/schemas/create-property.ts @@ -0,0 +1,6 @@ +import { Type } from "@sinclair/typebox"; + +export const createPropertySchema = Type.Object({ + name: Type.String(), + description: Type.Optional(Type.String()), +}); diff --git a/examples/common_nestjs_remix/apps/api/src/management/schemas/property.ts b/examples/common_nestjs_remix/apps/api/src/management/schemas/property.ts new file mode 100644 index 0000000..938801d --- /dev/null +++ b/examples/common_nestjs_remix/apps/api/src/management/schemas/property.ts @@ -0,0 +1,4 @@ +import { createSelectSchema } from "drizzle-typebox"; +import { properties } from "src/storage/schema"; + +export const propertySchema = createSelectSchema(properties); diff --git a/examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts b/examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts deleted file mode 100644 index 974f533..0000000 --- a/examples/common_nestjs_remix/apps/api/src/management/use-cases/create-property.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Controller, Inject, Post } from "@nestjs/common"; -import { CommandBus, CommandHandler, ICommandHandler } from "@nestjs/cqrs"; -import { type Static, Type } from "@sinclair/typebox"; -import { createSelectSchema } from "drizzle-typebox"; -import { Validate } from "nestjs-typebox"; -import { DatabasePg } from "src/common"; -import { properties } from "src/storage/schema"; - -export class CreateProperty { - name: string; - description?: string; - - constructor(data: Partial) { - Object.assign(this, data); - } -} - -const requestSchema = Type.Object({ - name: Type.String(), - description: Type.Optional(Type.String()), -}); -const responseSchema = createSelectSchema(properties); - -@Controller("properties") -export class CreatePropertyController { - constructor(private readonly commandBus: CommandBus) {} - - @Post() - @Validate({ - response: responseSchema, - request: [{ type: "body", schema: requestSchema }], - }) - async createProperty(data: Static) { - const command = new CreateProperty(data); - return this.commandBus.execute(command); - } -} - -@CommandHandler(CreateProperty) -export class CreatePropertyHandler implements ICommandHandler { - constructor(@Inject("DB") private readonly db: DatabasePg) {} - - async execute(command: CreateProperty) { - const [property] = await this.db - .insert(properties) - .values(command) - .returning(); - - return property; - } -} diff --git a/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts b/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts index 08e6991..10ea1bb 100644 --- a/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts +++ b/examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts @@ -1,4 +1,4 @@ -import { boolean, pgTable, text } from "drizzle-orm/pg-core"; +import { pgTable, text } from "drizzle-orm/pg-core"; import { id, timestamps } from "./utils"; export const users = pgTable("users", { diff --git a/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json b/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json index 3ed3930..f0c5769 100644 --- a/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json +++ b/examples/common_nestjs_remix/apps/api/src/swagger/api-schema.json @@ -3,7 +3,7 @@ "paths": { "/properties": { "post": { - "operationId": "CreatePropertyController_createProperty", + "operationId": "PropertiesController_createProperty", "parameters": [], "requestBody": { "required": true, @@ -27,6 +27,33 @@ } } } + }, + "/properties/{id}": { + "delete": { + "operationId": "PropertiesController_deleteProperty", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletePropertyResponse" + } + } + } + } + } + } } }, "info": { @@ -56,36 +83,47 @@ "CreatePropertyResponse": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "anyOf": [ - { + "data": { + "type": "object", + "properties": { + "id": { "type": "string" }, - { - "type": "null" + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] } + }, + "required": [ + "id", + "createdAt", + "updatedAt", + "name", + "description" ] } }, "required": [ - "id", - "createdAt", - "updatedAt", - "name", - "description" + "data" ] + }, + "DeletePropertyResponse": { + "type": "null" } } } From 63057f5ddf9abbb43f2fd65c8e11d94fd985b617 Mon Sep 17 00:00:00 2001 From: Tomasz Kielar Date: Mon, 8 Jul 2024 11:45:39 +0200 Subject: [PATCH 7/8] chore: fix api on fe --- .../apps/web/app/api/generated-api.ts | 32 +++++++++++++++---- .../app/api/mutations/useCreateProperty.ts | 2 +- .../web/app/modules/Landing/Pokemon.page.tsx | 8 +---- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts b/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts index 16e9ac1..9b3333f 100644 --- a/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts +++ b/examples/common_nestjs_remix/apps/web/app/api/generated-api.ts @@ -15,13 +15,17 @@ export interface CreatePropertyBody { } export interface CreatePropertyResponse { - id: string; - createdAt: string; - updatedAt: string; - name: string; - description: string | null; + data: { + id: string; + createdAt: string; + updatedAt: string; + name: string; + description: string | null; + }; } +export type DeletePropertyResponse = null; + import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from "axios"; import axios from "axios"; @@ -168,10 +172,10 @@ export class API extends HttpClient + propertiesControllerCreateProperty: (data: CreatePropertyBody, params: RequestParams = {}) => this.request({ path: `/properties`, method: "POST", @@ -180,5 +184,19 @@ export class API extends HttpClient + this.request({ + path: `/properties/${id}`, + method: "DELETE", + format: "json", + ...params, + }), }; } diff --git a/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts b/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts index 826a297..9d1b075 100644 --- a/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts +++ b/examples/common_nestjs_remix/apps/web/app/api/mutations/useCreateProperty.ts @@ -11,7 +11,7 @@ export function useCreateProperty() { return useMutation({ mutationFn: async (options: CreatePropertyOptions) => { const response = - await ApiClient.properties.createPropertyControllerCreateProperty( + await ApiClient.properties.propertiesControllerCreateProperty( options.data ); diff --git a/examples/common_nestjs_remix/apps/web/app/modules/Landing/Pokemon.page.tsx b/examples/common_nestjs_remix/apps/web/app/modules/Landing/Pokemon.page.tsx index 966336a..6818c11 100644 --- a/examples/common_nestjs_remix/apps/web/app/modules/Landing/Pokemon.page.tsx +++ b/examples/common_nestjs_remix/apps/web/app/modules/Landing/Pokemon.page.tsx @@ -3,7 +3,6 @@ import { ClientLoaderFunctionArgs, Form, isRouteErrorResponse, - useFormAction, useParams, useRouteError, } from "@remix-run/react"; @@ -11,13 +10,8 @@ import { useState } from "react"; import { invalidatePokemonQueries, updatePokemon, - useUpdatePokemon, } from "~/api/mutations/useUpdatePokemon"; -import { - pokemonOptions, - usePokemon, - usePokemonSuspense, -} from "~/api/queries/usePokemon"; +import { pokemonOptions, usePokemonSuspense } from "~/api/queries/usePokemon"; import { queryClient } from "~/api/queryClient"; export async function clientLoader({ params }: ClientLoaderFunctionArgs) { From b9ad2fedb57218ff337edcd2b55a4924545b0d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stas=CC=81kiewicz?= Date: Mon, 8 Jul 2024 11:56:44 +0200 Subject: [PATCH 8/8] chore: update .env.example --- examples/common_nestjs_remix/apps/api/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/common_nestjs_remix/apps/api/.env.example b/examples/common_nestjs_remix/apps/api/.env.example index 01ec508..0dae0a4 100644 --- a/examples/common_nestjs_remix/apps/api/.env.example +++ b/examples/common_nestjs_remix/apps/api/.env.example @@ -1 +1 @@ -DATABASE_URL="" +DATABASE_URL="postgres://postgres:guidebook@localhost:5432/guidebook"