@nest-arch/tui 0.2.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +20 -42
  2. package/dist/cli.js +1 -1
  3. package/dist/constants-C3xmDWeK.js +1 -0
  4. package/dist/index.js +295 -82
  5. package/dist/templates/base/.env.example.hbs +4 -2
  6. package/dist/templates/base/.env.hbs +4 -2
  7. package/dist/templates/base/.gitignore.hbs +2 -1
  8. package/dist/templates/base/.yarnrc.yml.hbs +0 -5
  9. package/dist/templates/base/README.md.hbs +33 -5
  10. package/dist/templates/base/nest-cli.json.hbs +2 -1
  11. package/dist/templates/base/pnpm-workspace.yaml.hbs +9 -0
  12. package/dist/templates/base/prisma.config.ts.hbs +32 -6
  13. package/dist/templates/base/src/app.controller.spec.ts.hbs +3 -11
  14. package/dist/templates/base/src/app.controller.ts.hbs +2 -2
  15. package/dist/templates/base/src/app.module.ts.hbs +19 -1
  16. package/dist/templates/base/src/app.router.ts.hbs +2 -2
  17. package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +7 -0
  18. package/dist/templates/base/src/database/adapters/mongo/prisma-mongo.adapter.ts.hbs +25 -1
  19. package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +17 -12
  20. package/dist/templates/base/src/database/adapters/sql/drizzle-sql.adapter.ts.hbs +9 -0
  21. package/dist/templates/base/src/database/adapters/sql/prisma-sql.adapter.ts.hbs +7 -4
  22. package/dist/templates/base/src/database/adapters/sql/raw-sql.adapter.ts.hbs +37 -0
  23. package/dist/templates/base/src/database/adapters/sql/typeorm-sql.adapter.ts.hbs +8 -3
  24. package/dist/templates/base/src/database/db.module.ts.hbs +32 -35
  25. package/dist/templates/base/src/database/db.service.ts.hbs +40 -6
  26. package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +12 -7
  27. package/dist/templates/base/src/database/index.ts.hbs +1 -0
  28. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +47 -5
  29. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -4
  30. package/dist/templates/base/src/libs/trpc/error.handler.ts.hbs +1 -1
  31. package/dist/templates/base/src/main.ts.hbs +21 -6
  32. package/dist/templates/base/test/app.e2e-spec.ts.hbs +208 -6
  33. package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +74 -0
  34. package/dist/templates/base/tsconfig.build.json.hbs +2 -0
  35. package/dist/templates/base/tsconfig.json.hbs +1 -1
  36. package/dist/templates/base/vitest.config.e2e.ts.hbs +4 -3
  37. package/dist/templates/base/vitest.config.ts.hbs +4 -3
  38. package/dist/templates/base/yarn.lock.hbs +1 -0
  39. package/dist/templates/extras/docker/Dockerfile.hbs +4 -1
  40. package/dist/templates/extras/swagger/create-docs.ts.hbs +47 -119
  41. package/dist/templates/feature/graphql/src/graphql/graphql.module.ts.hbs +8 -1
  42. package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +4 -0
  43. package/dist/templates/feature/graphql/src/graphql/orm/drizzle/drizzle-graphql.module.ts.hbs +4 -3
  44. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +69 -24
  45. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +3 -1
  46. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.module.ts.hbs +1 -3
  47. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +98 -32
  48. package/dist/templates/monorepo/apps/api-gateway/package.json.hbs +0 -1
  49. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.spec.ts.hbs +1 -1
  50. package/dist/templates/monorepo/apps/api-gateway/test/app.e2e-spec.ts.hbs +20 -1
  51. package/dist/templates/monorepo/apps/api-gateway/tsconfig.build.json.hbs +2 -1
  52. package/dist/templates/monorepo/apps/api-gateway/vitest.config.e2e.ts.hbs +3 -2
  53. package/dist/templates/monorepo/apps/api-gateway/vitest.config.ts.hbs +3 -2
  54. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +24 -21
  55. package/dist/templates/monorepo/packages/env/src/api-gateway.ts.hbs +9 -3
  56. package/dist/templates/monorepo/root/.gitignore.hbs +1 -0
  57. package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +9 -0
  58. package/dist/templates/monorepo/root/tsconfig.json.hbs +1 -1
  59. package/dist/templates/monorepo/root/turbo.json.hbs +2 -0
  60. package/dist/templates/single-project/architecture/nest-api/.env.hbs +3 -1
  61. package/dist/templates/single-project/architecture/nest-api/package.json.hbs +0 -1
  62. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +20 -5
  63. package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +18 -0
  64. package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
  65. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +14 -1
  66. package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +4 -0
  67. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +13 -2
  68. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +64 -0
  69. package/dist/templates/single-project/shared/config/env.ts.hbs +27 -3
  70. package/package.json +7 -4
  71. package/dist/constants-Cgtoxhmd.js +0 -1
@@ -13,7 +13,7 @@ POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/{{projectNameKebab}}_
13
13
  MYSQL_URL=mysql://root:mysql@localhost:3306/{{projectNameKebab}}_db
14
14
  {{/if}}
15
15
  {{#if hasSqlite}}
16
- SQLITE_PATH=file:./src/database/dev.db
16
+ SQLITE_PATH=./src/database/dev.db
17
17
  {{/if}}
18
18
  {{#if hasMongodb}}
19
19
  MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
@@ -24,11 +24,13 @@ MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
24
24
  GRAPHQL_PLAYGROUND=true
25
25
 
26
26
  {{/if}}
27
- {{#if (and (eq auth "passport") hasDatabase)}}
27
+ {{#if (eq auth "passport")}}
28
28
  # JWT
29
29
  JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
30
+ {{#if hasDatabase}}
30
31
  JWT_REFRESH_SECRET=change-me-to-a-different-random-secret-at-least-32-characters
31
32
  {{/if}}
33
+ {{/if}}
32
34
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
33
35
  # Better Auth
34
36
  BETTER_AUTH_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
@@ -11,15 +11,17 @@ POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/{{projectNameKebab}}_
11
11
  MYSQL_URL=mysql://root:mysql@localhost:3306/{{projectNameKebab}}_db
12
12
  {{/if}}
13
13
  {{#if hasSqlite}}
14
- SQLITE_PATH=file:./src/database/dev.db
14
+ SQLITE_PATH=./src/database/dev.db
15
15
  {{/if}}
16
16
  {{#if hasMongodb}}
17
17
  MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
18
18
  {{/if}}
19
- {{#if (and (eq auth "passport") hasDatabase)}}
19
+ {{#if (eq auth "passport")}}
20
20
  JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
21
+ {{#if hasDatabase}}
21
22
  JWT_REFRESH_SECRET=change-me-to-a-different-random-secret-at-least-32-characters
22
23
  {{/if}}
24
+ {{/if}}
23
25
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
24
26
  BETTER_AUTH_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
25
27
  BETTER_AUTH_URL=http://localhost:3000
@@ -43,6 +43,7 @@ lerna-debug.log*
43
43
 
44
44
  # dotenv environment variable files
45
45
  .env
46
+ .env.test
46
47
  .env.development.local
47
48
  .env.test.local
48
49
  .env.production.local
@@ -59,4 +60,4 @@ pids
59
60
  *.pid.lock
60
61
 
61
62
  # Diagnostic reports (https://nodejs.org/api/report.html)
62
- report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
63
+ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
@@ -1,6 +1 @@
1
1
  nodeLinker: node-modules
2
- catalog:
3
- "@types/node": ^22.13.14
4
- dotenv: ^17.2.2
5
- typescript: ^6.0.2
6
- zod: ^4.4.3
@@ -79,12 +79,18 @@ This project uses **TypeORM** as the ORM. Available commands:
79
79
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
80
80
  ## Auth Setup
81
81
 
82
- This project uses **Better Auth**. Generate the database schema by running:
82
+ This project uses **Better Auth** with database-backed sessions. Set `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` in `.env` before running the generated scripts.
83
83
 
84
- ```bash
85
- {{#if isPnpm}}pnpm dlx auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y && pnpm run prisma:format{{else if isYarn}}yarn dlx auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y && yarn run prisma:format{{else if isBun}}bun x auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y && bun run prisma:format{{else}}npx auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y && npm run prisma:format{{/if}}
86
- ```
87
- See https://better-auth.com/docs/adapters/prisma for more details.
84
+ Run `auth:generate` with your package manager. The script uses the installed CLI version.
85
+ {{#if usePrisma}}
86
+ Then run `prisma:generate`, `prisma:migrate --name auth`, and `build`. For deployments, apply the committed migrations with `prisma:migrate:deploy`. Auth models live in `src/database/orm/prisma/auth.prisma`; keep application models in `schema.prisma` or other files in that directory. `auth:generate` replaces only the auth-owned file (using `auth-schema.prisma` as CLI output). Inspect that diff after changing plugins.
87
+ {{else if useDrizzle}}
88
+ Then run `db:generate`, `db:migrate`, and `build`. Auth owns `src/database/orm/drizzle/auth-schema.ts`; application tables remain in `schema.ts`. Commit generated migration files. SQLite requires no Docker service.
89
+ {{else}}
90
+ Then run `auth:migrate` and `build`.
91
+ {{/if}}
92
+
93
+ The runtime auth module receives the database client owned by Nest. CLI schema discovery uses separate configuration. `GET /api/auth-profile` is a protected example for the Prisma and Drizzle profiles.
88
94
 
89
95
  ### Usage
90
96
 
@@ -185,3 +191,25 @@ import { AppRouterClass as AppRouter } from "./app.router.js";
185
191
  export class AppModule {}
186
192
  ```
187
193
  {{/if}}
194
+
195
+ {{#if isPassportAuth}}
196
+ ## Passport authentication
197
+
198
+ Set a random `JWT_ACCESS_SECRET` of at least 32 characters. Configure `JWT_ISSUER`, `JWT_AUDIENCE`, and `JWT_KEY_ID` consistently across instances. Apply Prisma migrations before starting.
199
+
200
+ Register with `POST /api/auth/register` and sign in with `POST /api/auth/login`, using `{ "email": "user@example.com", "password": "a-long-unique-password" }`. Send the returned access token in `Authorization: Bearer <token>` to `GET /api/auth/profile`. Access tokens expire after 15 minutes. Passwords are hashed with bcrypt; token payloads contain no password or role supplied by the client.
201
+
202
+ Refresh with `POST /api/auth/refresh` and `{ "refreshToken": "..." }`. Refresh tokens rotate on every use and expire seven days after login. Reusing a consumed token revokes the entire login session, including access tokens. Concurrent refresh requests therefore require client-side serialization. `POST /api/auth/logout` takes the same body and revokes that login session. Tokens are returned in JSON; no cookie transport is generated.
203
+
204
+ Only hashes of refresh tokens are stored. Schedule deletion of expired refresh-token families according to your retention policy. Secret replacement invalidates existing access tokens; overlapping signing keys are not implemented in this baseline.
205
+ {{/if}}
206
+ {{#if hasGraphql}}
207
+ ## GraphQL
208
+
209
+ Send queries to `POST /graphql`. `{ hello }` is always available. With the Todo example, `todos`, `createTodo`, `updateTodo`, and `deleteTodo` operate on PostgreSQL. The first profile is unauthenticated; it does not include subscriptions or federation.
210
+ {{/if}}
211
+ {{#if hasMongodb}}
212
+ ## MongoDB
213
+
214
+ Set `MONGODB_URL` including the database name. The native client connects and pings MongoDB during startup and closes with the Nest application. No Prisma migration commands are required. Todo identifiers are serialized ObjectId strings; malformed IDs return 400 and missing mutation targets return 404. Listing returns at most 100 items in identifier order. This baseline performs single-document writes and does not promise multi-document transactions.
215
+ {{/if}}
@@ -3,6 +3,7 @@
3
3
  "collection": "@nestjs/schematics",
4
4
  "sourceRoot": "src",
5
5
  "compilerOptions": {
6
- "deleteOutDir": true
6
+ "deleteOutDir": true{{#if (includes microservices "grpc")}},
7
+ "assets": ["proto/**/*"]{{/if}}
7
8
  }
8
9
  }
@@ -1,4 +1,8 @@
1
1
  allowBuilds:
2
+ {{#if useDrizzle}}
3
+ esbuild: true
4
+ {{/if}}
5
+ protobufjs: true
2
6
  "@nestjs/core": true
3
7
  {{#if swagger}}
4
8
  "@scarf/scarf": true
@@ -9,6 +13,11 @@ allowBuilds:
9
13
  {{#if usePrisma}}
10
14
  prisma: true
11
15
  "@prisma/engines": true
16
+ {{#if isPrisma8}}
17
+ esbuild: true
18
+ msgpackr-extract: true
19
+ workerd: true
20
+ {{/if}}
12
21
  {{/if}}
13
22
  {{#if (and (eq auth "passport") hasDatabase)}}
14
23
  bcrypt: true
@@ -1,11 +1,37 @@
1
- import "dotenv/config";
2
- import { defineConfig, env } from "prisma/config";
1
+ {{#if isMonorepo}}
2
+ import { env } from "@repo/env/api-gateway";
3
+ {{else}}
4
+ import { env } from "./src/config/env.js";
5
+ {{/if}}
6
+ {{#if isPrisma8}}
7
+ import { definePrismaConfig } from "prisma/config";
8
+ {{#if hasMongodb}}
9
+ import { defineConfig as ormConfig } from "@prisma/orm-mongo/config";
10
+ {{else}}
11
+ import { defineConfig as ormConfig } from "@prisma/orm-postgres/config";
12
+ {{/if}}
13
+
14
+ export default definePrismaConfig({
15
+ skills: {
16
+ agents: ["claude", "cursor", "agents", "devin"],
17
+ },
18
+ orm: ormConfig({
19
+ contract: "./src/prisma/contract.prisma",
20
+ db: {
21
+ connection: {{#if hasMongodb}}env.MONGODB_URL{{else}}env.POSTGRES_URL{{/if}},
22
+ },
23
+ }),
24
+ });
25
+ {{else}}
26
+ import { defineConfig } from "prisma/config";
3
27
 
4
28
  export default defineConfig({
5
- schema: "src/database/orm/prisma/schema.prisma",
29
+ schema: "src/database/orm/prisma{{#unless isBetterAuth}}/schema.prisma{{/unless}}",
6
30
  datasource: {
7
- {{#if hasPostgres}}url: env("POSTGRES_URL"),{{/if}}
8
- {{#if hasMysql}}url: env("MYSQL_URL"),{{/if}}
9
- {{#if hasSqlite}}url: env("SQLITE_PATH"),{{/if}}
31
+ {{#if hasPostgres}}url: env.POSTGRES_URL,{{/if}}
32
+ {{#if hasMysql}}url: env.MYSQL_URL,{{/if}}
33
+ {{#if hasSqlite}}url: env.SQLITE_PATH,{{/if}}
34
+ {{#if hasMongodb}}url: env.MONGODB_URL,{{/if}}
10
35
  },
11
36
  });
37
+ {{/if}}
@@ -1,22 +1,15 @@
1
1
  // oxlint-disable vitest/require-mock-type-parameters
2
2
  import type { TestingModule } from "@nestjs/testing";
3
3
  import { Test } from "@nestjs/testing";
4
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { beforeEach, describe, expect, it } from "vitest";
5
5
 
6
6
  import { AppController } from "./app.controller.js";
7
7
  import { AppService } from "./app.service.js";
8
8
  {{#if (or hasDatabase hasOrm)}}
9
- import type { ISqlAdapter } from "./database/adapters/interfaces/db-adapter.interface.js";
10
9
  import { DbService } from "./database/db.service.js";
11
10
  {{/if}}
12
11
 
13
- {{#if (or hasDatabase hasOrm)}}
14
- const mockSqlAdapter: ISqlAdapter = {
15
- execute: vi.fn().mockResolvedValue(null),
16
- getClient: vi.fn().mockReturnValue({}),
17
- query: vi.fn().mockResolvedValue([]),
18
- };
19
- {{/if}}
12
+
20
13
 
21
14
  describe(AppController, () => {
22
15
  let appController: AppController;
@@ -27,8 +20,7 @@ describe(AppController, () => {
27
20
  providers: [
28
21
  AppService,
29
22
  {{#if (or hasDatabase hasOrm)}}
30
- DbService,
31
- { provide: "SQL_ADAPTER", useValue: mockSqlAdapter },
23
+ { provide: DbService, useValue: {} },
32
24
  {{/if}}
33
25
  ],
34
26
  }).compile();
@@ -19,10 +19,10 @@ export class AppController {
19
19
  {{/if}}
20
20
  {{#if swagger}}
21
21
  @ApiOperation({ summary: "/api" })
22
- @ApiResponse({ status: 200, description: "Returns a hello world message" })
22
+ @ApiResponse({ description: "Returns a hello world message", status: 200 })
23
23
  {{/if}}
24
24
  @Get()
25
25
  getHello(): string {
26
26
  return this.appService.getHello();
27
27
  }
28
- }
28
+ }
@@ -1,4 +1,7 @@
1
1
  import { Module } from "@nestjs/common";
2
+ {{#if (and isPassportAuth hasDatabase)}}
3
+ import { PassportModule } from "@nestjs/passport";
4
+ {{/if}}
2
5
  {{#if (or hasDatabase hasOrm)}}
3
6
  import { env } from "./config/env.js";
4
7
  import { DbModule } from "./database/db.module.js";
@@ -6,6 +9,12 @@ import { DbModule } from "./database/db.module.js";
6
9
  {{#if shouldImportAuthModule}}
7
10
  import { AuthModule } from "./auth/auth.module.js";
8
11
  {{/if}}
12
+ {{#if (and isPassportAuth hasDatabase)}}
13
+ {{#if hasGraphql}}
14
+ import { GqlAuthGuard } from "./auth/guards/gql-auth.guard.js";
15
+ {{/if}}
16
+ import { RestAuthGuard } from "./auth/guards/rest-auth.guard.js";
17
+ {{/if}}
9
18
  {{#if hasGraphql}}
10
19
  import { GraphqlAppModule } from "./graphql/graphql.module.js";
11
20
  {{/if}}
@@ -48,6 +57,9 @@ import { AppService } from "./app.service.js";
48
57
  {{#if shouldImportAuthModule}}
49
58
  AuthModule,
50
59
  {{/if}}
60
+ {{#if (and isPassportAuth hasDatabase)}}
61
+ PassportModule.register({ defaultStrategy: "jwt" }),
62
+ {{/if}}
51
63
  {{#if hasGraphql}}
52
64
  GraphqlAppModule,
53
65
  {{/if}}
@@ -63,6 +75,12 @@ import { AppService } from "./app.service.js";
63
75
  ],
64
76
  providers: [
65
77
  AppService,
78
+ {{#if (and isPassportAuth hasDatabase)}}
79
+ {{#if hasGraphql}}
80
+ GqlAuthGuard,
81
+ {{/if}}
82
+ RestAuthGuard,
83
+ {{/if}}
66
84
  {{#if hasTrpc}}
67
85
  {{#if hasTrpcAndAuth}}
68
86
  AuthTRPCMiddleware,
@@ -76,4 +94,4 @@ import { AppService } from "./app.service.js";
76
94
  {{/if}}
77
95
  ],
78
96
  })
79
- export class AppModule {}
97
+ export class AppModule {}
@@ -1,5 +1,5 @@
1
- import { Router, Query, UseMiddlewares, Ctx } from "nestjs-trpc";
2
- import z from "zod";
1
+ import { Router, Query{{#if hasTrpcAndAuth}}, UseMiddlewares, Ctx{{/if}} } from "nestjs-trpc";
2
+ import { z } from "zod";
3
3
  {{#if hasTrpcAndAuth}}
4
4
  import type { UserSession } from "./auth/auth.js";
5
5
  import { AuthTRPCMiddleware } from "./libs/middlewares/auth.trpc.middleware.js";
@@ -7,8 +7,15 @@ export interface ISqlAdapter<TClient = unknown> {
7
7
  {{/if}}
8
8
 
9
9
  {{#if hasMongodb}}
10
+ {{#if useRawSql}}
11
+ import type { Collection, Document } from "mongodb";
12
+ {{/if}}
10
13
  export interface IMongoAdapter<TClient = unknown> {
14
+ {{#if useRawSql}}
15
+ collection<T extends Document = Document>(name: string): Collection<T>;
16
+ {{else}}
11
17
  collection<T = unknown>(name: string): T;
18
+ {{/if}}
12
19
  getClient(): TClient;
13
20
  }
14
21
  {{/if}}
@@ -1,13 +1,36 @@
1
1
  import { Injectable, Inject } from '@nestjs/common';
2
2
  import { IMongoAdapter } from '../interfaces/db-adapter.interface.js';
3
+ {{#if isPrisma8}}
4
+ {{#if isMonorepo}}
5
+ import { db } from '../../../prisma/db.js';
6
+ {{else}}
7
+ import { db } from '../../prisma/db.js';
8
+ {{/if}}
9
+ {{else}}
3
10
  {{#if isMonorepo}}
4
11
  import type { PrismaClient } from '../../../database/orm/prisma/generated/client.js';
5
12
  {{else}}
6
13
  import type { PrismaClient } from '../../orm/prisma/generated/client.js';
7
14
  {{/if}}
15
+ {{/if}}
8
16
 
9
17
  @Injectable()
10
- export class PrismaMongoAdapter implements IMongoAdapter<PrismaClient> {
18
+ export class PrismaMongoAdapter implements IMongoAdapter<any> {
19
+ {{#if isPrisma8}}
20
+ readonly client = db;
21
+
22
+ collection<T = unknown>(name: string): T {
23
+ return ((this.client as any).orm as any)?.[name] as T;
24
+ }
25
+
26
+ async runCommand(command: unknown): Promise<unknown> {
27
+ return (this.client as any).query?.(command);
28
+ }
29
+
30
+ getClient() {
31
+ return this.client;
32
+ }
33
+ {{else}}
11
34
  constructor(
12
35
  @Inject('PRISMA_MONGO_CLIENT') private readonly prisma: PrismaClient
13
36
  ) {}
@@ -22,4 +45,5 @@ export class PrismaMongoAdapter implements IMongoAdapter<PrismaClient> {
22
45
  getClient(): PrismaClient {
23
46
  return this.prisma;
24
47
  }
48
+ {{/if}}
25
49
  }
@@ -1,17 +1,22 @@
1
- import { Injectable, Inject } from '@nestjs/common';
2
- import { IMongoAdapter } from '../interfaces/db-adapter.interface.js';
1
+ import { Injectable, Inject, type OnModuleInit, type OnModuleDestroy } from "@nestjs/common";
2
+ import { MongoClient, type Collection, type Db, type Document } from "mongodb";
3
+ import type { IMongoAdapter } from "../interfaces/db-adapter.interface.js";
3
4
 
4
5
  @Injectable()
5
- export class RawMongoAdapter implements IMongoAdapter {
6
- constructor(
7
- @Inject('MONGODB_CLIENT') private readonly db: any
8
- ) {}
9
-
10
- collection(name: string): any {
11
- return this.db.collection(name);
6
+ export class RawMongoAdapter implements IMongoAdapter<Db>, OnModuleInit, OnModuleDestroy {
7
+ constructor(@Inject("MONGODB_CLIENT") private readonly client: MongoClient) {}
8
+ collection<T extends Document = Document>(name: string): Collection<T> {
9
+ return this.client.db().collection<T>(name);
12
10
  }
13
-
14
- getClient(): any {
15
- return this.db;
11
+ getClient(): Db { return this.client.db(); }
12
+ async onModuleInit(): Promise<void> {
13
+ try {
14
+ await this.client.connect();
15
+ await this.client.db().command({ ping: 1 });
16
+ } catch (error) {
17
+ await this.client.close();
18
+ throw error;
19
+ }
16
20
  }
21
+ async onModuleDestroy(): Promise<void> { await this.client.close(); }
17
22
  }
@@ -21,4 +21,13 @@ export class DrizzleSqlAdapter<TClient = unknown> implements ISqlAdapter<TClient
21
21
  getClient(): TClient {
22
22
  return this.db;
23
23
  }
24
+
25
+ async onModuleDestroy(): Promise<void> {
26
+ const client = (this.db as any).$client;
27
+ if (typeof client?.end === 'function') {
28
+ await client.end();
29
+ } else if (typeof client?.close === 'function') {
30
+ await client.close();
31
+ }
32
+ }
24
33
  }
@@ -1,4 +1,3 @@
1
- // oxlint-disable typescript/no-explicit-any require-await
2
1
  import { Injectable, Inject } from '@nestjs/common';
3
2
  import { ISqlAdapter } from '../interfaces/db-adapter.interface.js';
4
3
  {{#if isMonorepo}}
@@ -15,15 +14,19 @@ export class PrismaSqlAdapter implements ISqlAdapter<PrismaClient> {
15
14
  @Inject('PRISMA_SQL_CLIENT') private readonly prisma: PrismaClient
16
15
  ) {}
17
16
 
18
- async query<T = any>(sql: string, params: any[] = []): Promise<T[]> {
19
- return this.prisma.$queryRawUnsafe(sql, ...params);
17
+ query<T = unknown>(sql: string, params: unknown[] = []): Promise<T[]> {
18
+ return this.prisma.$queryRawUnsafe<T[]>(sql, ...params);
20
19
  }
21
20
 
22
- async execute(sql: string, params: any[] = []): Promise<any> {
21
+ execute(sql: string, params: unknown[] = []): Promise<number> {
23
22
  return this.prisma.$executeRawUnsafe(sql, ...params);
24
23
  }
25
24
 
26
25
  getClient(): PrismaClient {
27
26
  return this.prisma;
28
27
  }
28
+
29
+ async onModuleDestroy(): Promise<void> {
30
+ await this.prisma.$disconnect();
31
+ }
29
32
  }
@@ -1,3 +1,31 @@
1
+ {{#if hasPostgres}}
2
+ import { Inject, Injectable } from "@nestjs/common";
3
+ import type { Pool } from "pg";
4
+
5
+ import type { ISqlAdapter } from "../interfaces/db-adapter.interface.js";
6
+
7
+ @Injectable()
8
+ export class RawSqlAdapter implements ISqlAdapter<Pool> {
9
+ constructor(@Inject("SQL_CLIENT") private readonly db: Pool) {}
10
+
11
+ async query<T = unknown>(queryString: string, params: unknown[] = []): Promise<T[]> {
12
+ const result = await this.db.query(queryString, params);
13
+ return result.rows as T[];
14
+ }
15
+
16
+ execute(queryString: string, params: unknown[] = []): Promise<unknown> {
17
+ return this.db.query(queryString, params);
18
+ }
19
+
20
+ getClient(): Pool {
21
+ return this.db;
22
+ }
23
+
24
+ async onModuleDestroy(): Promise<void> {
25
+ await this.db.end();
26
+ }
27
+ }
28
+ {{else}}
1
29
  import { Injectable, Inject } from '@nestjs/common';
2
30
  import { ISqlAdapter } from '../interfaces/db-adapter.interface.js';
3
31
 
@@ -46,4 +74,13 @@ export class RawSqlAdapter implements ISqlAdapter {
46
74
  getClient(): any {
47
75
  return this.db;
48
76
  }
77
+
78
+ async onModuleDestroy(): Promise<void> {
79
+ if (typeof this.db.end === 'function') {
80
+ await this.db.end();
81
+ } else if (typeof this.db.close === 'function') {
82
+ await this.db.close();
83
+ }
84
+ }
49
85
  }
86
+ {{/if}}
@@ -1,4 +1,3 @@
1
- // oxlint-disable typescript/no-explicit-any require-await
2
1
  import { Injectable, Inject } from '@nestjs/common';
3
2
  import type { DataSource } from 'typeorm';
4
3
  import type { ISqlAdapter } from '../interfaces/db-adapter.interface.js';
@@ -9,15 +8,21 @@ export class TypeormSqlAdapter implements ISqlAdapter<DataSource> {
9
8
  @Inject('TYPEORM_DATA_SOURCE') private readonly dataSource: DataSource
10
9
  ) {}
11
10
 
12
- async query<T = any>(sql: string, params: any[] = []): Promise<T[]> {
11
+ query<T = unknown>(sql: string, params: unknown[] = []): Promise<T[]> {
13
12
  return this.dataSource.query(sql, params);
14
13
  }
15
14
 
16
- async execute(sql: string, params: any[] = []): Promise<any> {
15
+ execute(sql: string, params: unknown[] = []): Promise<unknown> {
17
16
  return this.dataSource.query(sql, params);
18
17
  }
19
18
 
20
19
  getClient(): DataSource {
21
20
  return this.dataSource;
22
21
  }
22
+
23
+ async onModuleDestroy(): Promise<void> {
24
+ if (this.dataSource.isInitialized) {
25
+ await this.dataSource.destroy();
26
+ }
27
+ }
23
28
  }
@@ -1,3 +1,15 @@
1
+ {{#if isPrisma8}}
2
+ import { DynamicModule, Global, Module } from "@nestjs/common";
3
+ import { DbService } from "./db.service.js";
4
+
5
+ @Global()
6
+ @Module({})
7
+ export class DbModule {
8
+ static forRoot(_options?: unknown): DynamicModule {
9
+ return { exports: [DbService], module: DbModule, providers: [DbService] };
10
+ }
11
+ }
12
+ {{else}}
1
13
  import { DynamicModule, Module, Global, Provider } from '@nestjs/common';
2
14
  import { DbService } from './db.service.js';
3
15
 
@@ -87,7 +99,8 @@ export interface DbModuleOptions {
87
99
  @Module({})
88
100
  export class DbModule {
89
101
  static forRoot(options: DbModuleOptions): DynamicModule {
90
- const providers: Provider[] = [DbService];
102
+ const initialProviders: Provider[] = [DbService];
103
+ const providers = initialProviders;
91
104
 
92
105
  {{#if usePrisma}}
93
106
  {{#if (or hasPostgres hasMysql hasSqlite)}}
@@ -98,7 +111,10 @@ export class DbModule {
98
111
  const prismaSql = new PrismaClient({ adapter: sqlAdapter });
99
112
  {{/if}}
100
113
  {{#if hasMysql}}
101
- const sqlAdapter = new PrismaMariaDb(options.mysqlUrl!);
114
+ if (!options.mysqlUrl) {
115
+ throw new Error("A MySQL URL is required to initialize Prisma");
116
+ }
117
+ const sqlAdapter = new PrismaMariaDb(options.mysqlUrl);
102
118
  const prismaSql = new PrismaClient({ adapter: sqlAdapter });
103
119
  {{/if}}
104
120
  {{#if hasSqlite}}
@@ -137,39 +153,20 @@ export class DbModule {
137
153
  {{/if}}
138
154
 
139
155
  {{#if useDrizzle}}
140
- {{#if hasPostgres}}
141
- const pool = new Pool({
142
- connectionString: options.postgresUrl,
143
- });
144
- const drizzleClient = drizzle({ client: pool, schema });
145
- providers.push({
146
- provide: 'DRIZZLE_CLIENT',
147
- useValue: drizzleClient,
148
- });
149
- {{/if}}
150
- {{#if hasMysql}}
151
- const pool = mysql.createPool({
152
- uri: options.mysqlUrl,
153
- });
154
- const drizzleClient = drizzle({ client: pool, schema });
155
- providers.push({
156
- provide: 'DRIZZLE_CLIENT',
157
- useValue: drizzleClient,
158
- });
159
- {{/if}}
160
- {{#if hasSqlite}}
161
- const sqliteDb = new Database(options.sqlitePath || ':memory:');
162
- const drizzleClient = drizzle({ client: sqliteDb, schema });
163
156
  providers.push({
164
157
  provide: 'DRIZZLE_CLIENT',
165
- useValue: drizzleClient,
166
- });
167
- {{/if}}
168
-
169
- providers.push({
170
- provide: 'SQL_ADAPTER',
171
- useClass: DrizzleSqlAdapter,
158
+ useFactory: () => {
159
+ {{#if hasPostgres}}
160
+ const client = new Pool({ connectionString: options.postgresUrl });
161
+ {{else if hasMysql}}
162
+ const client = mysql.createPool({ uri: options.mysqlUrl });
163
+ {{else}}
164
+ const client = new Database(options.sqlitePath || ':memory:');
165
+ {{/if}}
166
+ return drizzle({ client, schema });
167
+ },
172
168
  });
169
+ providers.push({ provide: 'SQL_ADAPTER', useClass: DrizzleSqlAdapter });
173
170
  {{/if}}
174
171
 
175
172
  {{#if useTypeorm}}
@@ -220,11 +217,10 @@ export class DbModule {
220
217
  {{/if}}
221
218
 
222
219
  {{#if hasMongodb}}
223
- const mongoClient = new MongoClient(options.mongodbUrl || '');
224
- const mongoDb = mongoClient.db();
220
+ if (!options.mongodbUrl) throw new Error("A MongoDB URL is required");
225
221
  providers.push({
226
222
  provide: 'MONGODB_CLIENT',
227
- useValue: mongoDb,
223
+ useFactory: () => new MongoClient(options.mongodbUrl!, { serverSelectionTimeoutMS: 5000 }),
228
224
  });
229
225
  providers.push({
230
226
  provide: 'MONGO_ADAPTER',
@@ -240,3 +236,4 @@ export class DbModule {
240
236
  };
241
237
  }
242
238
  }
239
+ {{/if}}