@nest-arch/tui 0.4.5 → 0.5.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 (46) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/constants-DX5wiqrq.js +1 -0
  3. package/dist/index.js +158 -145
  4. package/dist/templates/addons/husky/noop-hook.hbs +2 -0
  5. package/dist/templates/addons/husky/pre-commit.hbs +1 -1
  6. package/dist/templates/base/.env.example.hbs +16 -2
  7. package/dist/templates/base/.env.hbs +16 -2
  8. package/dist/templates/base/README.md.hbs +23 -2
  9. package/dist/templates/base/pnpm-workspace.yaml.hbs +9 -4
  10. package/dist/templates/base/prisma.config.ts.hbs +6 -0
  11. package/dist/templates/base/src/app.module.ts.hbs +6 -22
  12. package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +1 -1
  13. package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +4 -2
  14. package/dist/templates/base/src/database/db.module.ts.hbs +30 -3
  15. package/dist/templates/base/src/database/db.service.ts.hbs +11 -8
  16. package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +2 -2
  17. package/dist/templates/base/src/database/index.ts.hbs +2 -0
  18. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +48 -12
  19. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +26 -10
  20. package/dist/templates/base/src/libs/middlewares/graphql-throttler.guard.ts.hbs +22 -0
  21. package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +2 -2
  22. package/dist/templates/base/src/main.ts.hbs +7 -6
  23. package/dist/templates/base/test/app.e2e-spec.ts.hbs +374 -198
  24. package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +2 -2
  25. package/dist/templates/extras/docker/Dockerfile.hbs +8 -1
  26. package/dist/templates/extras/health/src/health/health.controller.ts.hbs +7 -1
  27. package/dist/templates/extras/swagger/create-docs.ts.hbs +6 -1
  28. package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +1 -1
  29. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +1 -1
  30. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +80 -7
  31. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +181 -25
  32. package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +10 -9
  33. package/dist/templates/monorepo/root/README.md.hbs +8 -13
  34. package/dist/templates/single-project/architecture/nest-api/.env.hbs +15 -2
  35. package/dist/templates/single-project/architecture/nest-api/pnpm-workspace.yaml.hbs +6 -0
  36. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +10 -9
  37. package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +10 -9
  38. package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +6 -0
  39. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +2 -2
  40. package/dist/templates/single-project/architecture/nest-microservice/src/app.module.ts.hbs +28 -1
  41. package/dist/templates/single-project/architecture/nest-microservice/src/http-throttler.guard.ts.hbs +13 -0
  42. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +10 -9
  43. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +35 -0
  44. package/dist/templates/single-project/shared/config/env.ts.hbs +9 -0
  45. package/package.json +5 -5
  46. package/dist/constants-BbC7KTI7.js +0 -1
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exit 0
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
  set -e
3
3
 
4
- {{#if isPnpm}}pnpm test{{else if isYarn}}yarn test{{else if isBun}}bun run test{{else}}npm test{{/if}}
4
+ {{#if isPnpm}}pnpm test --passWithNoTests{{else if isYarn}}yarn test --passWithNoTests{{else if isBun}}bun run test --passWithNoTests{{else}}npm test -- --passWithNoTests{{/if}}
5
5
 
6
6
  {{#if hasFormatter}}
7
7
  STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
@@ -1,7 +1,7 @@
1
1
  # Server
2
2
  PORT=3000
3
3
  NODE_ENV=development
4
- CORS_ORIGIN=http://localhost:3001
4
+ CORS_ORIGIN=http://localhost:3000
5
5
  # Comma-separated trusted reverse proxy IP addresses or CIDRs
6
6
  TRUSTED_PROXY_IPS={{#each trustedProxyIps}}{{#unless @first}},{{/unless}}{{this}}{{/each}}
7
7
 
@@ -17,6 +17,15 @@ MYSQL_URL=mysql://root:mysql@localhost:3306/{{projectNameKebab}}_db
17
17
  {{#if hasSqlite}}
18
18
  SQLITE_PATH=./src/database/dev.db
19
19
  {{/if}}
20
+ {{#if hasSqlserver}}
21
+ SQLSERVER_HOST=localhost
22
+ SQLSERVER_PORT=1433
23
+ SQLSERVER_DATABASE={{projectNameKebab}}_db
24
+ SQLSERVER_USER=sa
25
+ SQLSERVER_PASSWORD=DevOnly_Strong_Password_2026!X9
26
+ SQLSERVER_ENCRYPT=true
27
+ SQLSERVER_TRUST_SERVER_CERTIFICATE=true
28
+ {{/if}}
20
29
  {{#if hasMongodb}}
21
30
  MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
22
31
  {{/if}}
@@ -26,7 +35,12 @@ MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
26
35
  GRAPHQL_PLAYGROUND=true
27
36
 
28
37
  {{/if}}
29
- {{#if (eq auth "passport")}}
38
+ {{#if (and (eq auth "passport") (not hasDatabase))}}
39
+ # External identity provider (RS256 JWKS)
40
+ OIDC_JWKS_URI=https://identity.example.com/.well-known/jwks.json
41
+ OIDC_ISSUER=https://identity.example.com/
42
+ OIDC_AUDIENCE=generated-resource-server
43
+ {{else if (eq auth "passport")}}
30
44
  # JWT
31
45
  JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
32
46
  {{#if hasDatabase}}
@@ -1,4 +1,4 @@
1
- CORS_ORIGIN=http://localhost:3001
1
+ CORS_ORIGIN=http://localhost:3000
2
2
  PORT=3000
3
3
  TRUSTED_PROXY_IPS={{#each trustedProxyIps}}{{#unless @first}},{{/unless}}{{this}}{{/each}}
4
4
  {{#if hasGraphql}}
@@ -14,10 +14,24 @@ MYSQL_URL=mysql://root:mysql@localhost:3306/{{projectNameKebab}}_db
14
14
  {{#if hasSqlite}}
15
15
  SQLITE_PATH=./src/database/dev.db
16
16
  {{/if}}
17
+ {{#if hasSqlserver}}
18
+ SQLSERVER_HOST=localhost
19
+ SQLSERVER_PORT=1433
20
+ SQLSERVER_DATABASE={{projectNameKebab}}_db
21
+ SQLSERVER_USER=sa
22
+ SQLSERVER_PASSWORD=DevOnly_Strong_Password_2026!X9
23
+ SQLSERVER_ENCRYPT=true
24
+ SQLSERVER_TRUST_SERVER_CERTIFICATE=true
25
+ {{/if}}
17
26
  {{#if hasMongodb}}
18
27
  MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
19
28
  {{/if}}
20
- {{#if (eq auth "passport")}}
29
+ {{#if (and (eq auth "passport") (not hasDatabase))}}
30
+ # External identity provider (RS256 JWKS)
31
+ OIDC_JWKS_URI=https://identity.example.com/.well-known/jwks.json
32
+ OIDC_ISSUER=https://identity.example.com/
33
+ OIDC_AUDIENCE=generated-resource-server
34
+ {{else if (eq auth "passport")}}
21
35
  JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
22
36
  {{#if hasDatabase}}
23
37
  JWT_REFRESH_SECRET=change-me-to-a-different-random-secret-at-least-32-characters
@@ -4,10 +4,14 @@
4
4
 
5
5
  ## Setup
6
6
 
7
+ {{#if setupInstructions}}
8
+ {{{setupInstructions}}}
9
+ {{else}}
7
10
  ```bash
8
11
  # Install dependencies
9
12
  {{#if isPnpm}}pnpm install{{else if isYarn}}yarn install{{else if isBun}}bun install{{else}}npm install{{/if}}
10
13
  ```
14
+ {{/if}}
11
15
 
12
16
  ## Running the App
13
17
 
@@ -54,6 +58,10 @@ This project uses **Prisma** as the ORM. Available commands:
54
58
  {{#if isPnpm}}pnpm prisma:migrate{{else if isYarn}}yarn prisma:migrate{{else if isBun}}bun run prisma:migrate{{else}}npm run prisma:migrate{{/if}}
55
59
  {{#if isPnpm}}pnpm prisma:migrate:deploy{{else if isYarn}}yarn prisma:migrate:deploy{{else if isBun}}bun run prisma:migrate:deploy{{else}}npm run prisma:migrate:deploy{{/if}}
56
60
  ```
61
+ {{#if hasSqlserver}}
62
+
63
+ Set `SQLSERVER_HOST`, `SQLSERVER_PORT`, `SQLSERVER_DATABASE`, `SQLSERVER_USER`, and `SQLSERVER_PASSWORD` in `.env` before running Prisma commands. The SQL Server database must already exist. The generated password is a development-only example that satisfies SQL Server's password policy; replace it with a unique secret before sharing the project or deploying it. For local development, set `SQLSERVER_TRUST_SERVER_CERTIFICATE=true` when using a self-signed certificate; keep certificate validation enabled for production.
64
+ {{/if}}
57
65
  {{/if}}
58
66
 
59
67
  {{#if useDrizzle}}
@@ -94,9 +102,14 @@ This project uses **Better Auth** with database-backed sessions. Set `BETTER_AUT
94
102
 
95
103
  Run `auth:generate` with your package manager. The script uses the installed CLI version.
96
104
  {{#if usePrisma}}
97
- 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.
105
+ {{#if hasMongodb}}
106
+ `auth:generate` normalizes MongoDB IDs and writes auth models to `src/database/orm/prisma/auth.prisma`, using `auth-schema.prisma` as intermediate CLI output. Then run `prisma:push` and `prisma:generate`, as shown in Setup.
107
+ {{else}}
108
+ `auth:generate` updates auth models in `src/database/orm/prisma/schema.prisma`. For local development, run `prisma:push` and then `prisma:generate`, as shown in Setup. For a workflow with migration history, use `prisma:migrate --name auth` instead of pushing the schema and then regenerate the client. For deployments, apply the committed migrations with `prisma:migrate:deploy`.
109
+ {{/if}}
110
+ Inspect the schema diff after changing plugins.
98
111
  {{else if useDrizzle}}
99
- 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.
112
+ For local development, run `db:push`, as shown in Setup. For a workflow with migration history, use `db:generate` and `db:migrate` instead. Auth owns `src/database/orm/drizzle/auth-schema.ts`; application tables remain in `schema.ts`. Commit generated migration files. SQLite requires no Docker service.
100
113
  {{else}}
101
114
  Then run `auth:migrate` and `build`.
102
115
  {{/if}}
@@ -229,3 +242,11 @@ Send queries to `POST /graphql`. `{ hello }` is always available. With the Todo
229
242
 
230
243
  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.
231
244
  {{/if}}
245
+ {{#if docker}}
246
+ ## Docker Compose
247
+
248
+ Start the application and its database with `docker compose up --build`. Compose publishes the API on `127.0.0.1:3000` by default (Fastify uses `3001`). If that host port is already in use or reserved, set `APP_PORT` to a free host port before starting; the container's internal port stays unchanged. In PowerShell, for example: `$env:APP_PORT = "3100"; docker compose up --build`.
249
+ {{#if hasSqlserver}}
250
+ Compose also publishes SQL Server on `127.0.0.1:1433` so Prisma commands run on the host can connect. The API container uses the internal `sqlserver:1433` address. If host port 1433 is occupied, change `SQLSERVER_PORT` in `.env`; keep the API container's internal database port at 1433.
251
+ {{/if}}
252
+ {{/if}}
@@ -1,12 +1,9 @@
1
1
  allowBuilds:
2
- {{#if useDrizzle}}
2
+ {{#if (or useDrizzle useTypescript7)}}
3
3
  esbuild: true
4
4
  {{/if}}
5
5
  protobufjs: true
6
6
  "@nestjs/core": true
7
- {{#if useTypescript7}}
8
- esbuild: true
9
- {{/if}}
10
7
  {{#if swagger}}
11
8
  "@scarf/scarf": true
12
9
  {{/if}}
@@ -20,7 +17,9 @@ allowBuilds:
20
17
  "@prisma/client": true
21
18
  {{/if}}
22
19
  {{#if isPrisma8}}
20
+ {{#unless (or useDrizzle useTypescript7)}}
23
21
  esbuild: true
22
+ {{/unless}}
24
23
  msgpackr-extract: true
25
24
  workerd: true
26
25
  {{/if}}
@@ -31,3 +30,9 @@ allowBuilds:
31
30
  {{#if hasSqlite}}
32
31
  better-sqlite3: true
33
32
  {{/if}}
33
+ {{#if hasTrpc}}
34
+ peerDependencyRules:
35
+ allowedVersions:
36
+ "nestjs-trpc>@nestjs/common": "^12.0.0"
37
+ "nestjs-trpc>@nestjs/core": "^12.0.0"
38
+ {{/if}}
@@ -27,10 +27,16 @@ import { defineConfig } from "prisma/config";
27
27
 
28
28
  export default defineConfig({
29
29
  schema: "src/database/orm/prisma{{#unless isBetterAuth}}/schema.prisma{{/unless}}",
30
+ {{#unless isMonorepo}}
31
+ migrations: {
32
+ path: "src/database/orm/prisma/migrations",
33
+ },
34
+ {{/unless}}
30
35
  datasource: {
31
36
  {{#if hasPostgres}}url: env.POSTGRES_URL,{{/if}}
32
37
  {{#if hasMysql}}url: env.MYSQL_URL,{{/if}}
33
38
  {{#if hasSqlite}}url: env.SQLITE_PATH,{{/if}}
39
+ {{#if hasSqlserver}}url: `sqlserver://${env.SQLSERVER_HOST}:${env.SQLSERVER_PORT};database=${env.SQLSERVER_DATABASE};user={${env.SQLSERVER_USER.replaceAll("}", "}}")}};password={${env.SQLSERVER_PASSWORD.replaceAll("}", "}}")}};encrypt=${env.SQLSERVER_ENCRYPT};trustServerCertificate=${env.SQLSERVER_TRUST_SERVER_CERTIFICATE}`,{{/if}}
34
40
  {{#if hasMongodb}}url: env.MONGODB_URL,{{/if}}
35
41
  },
36
42
  });
@@ -1,7 +1,4 @@
1
- import { Module{{#if (and hasGraphql hasRateLimiting)}}, Injectable, type ExecutionContext{{/if}} } from "@nestjs/common";
2
- {{#if (and hasGraphql hasRateLimiting)}}
3
- import { GqlExecutionContext } from "@nestjs/graphql";
4
- {{/if}}
1
+ import { Module } from "@nestjs/common";
5
2
  {{#if (and isPassportAuth hasDatabase)}}
6
3
  import { PassportModule } from "@nestjs/passport";
7
4
  {{/if}}
@@ -50,30 +47,16 @@ import { HealthModule } from "./health/health.module.js";
50
47
  {{/if}}
51
48
  {{#if hasRateLimiting}}
52
49
  import { APP_GUARD } from "@nestjs/core";
53
- import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
50
+ import { {{#unless hasGraphql}}ThrottlerGuard, {{/unless}}ThrottlerModule } from "@nestjs/throttler";
51
+ {{/if}}
52
+ {{#if (and hasGraphql hasRateLimiting)}}
53
+ import { GraphqlThrottlerGuard } from "./libs/middlewares/graphql-throttler.guard.js";
54
54
  {{/if}}
55
55
  {{#if (and isPassportAuth hasDatabase hasGraphql (not hasRateLimiting))}}
56
56
  import { APP_GUARD } from "@nestjs/core";
57
57
  {{/if}}
58
58
  import { AppService } from "./app.service.js";
59
59
 
60
- {{#if (and hasGraphql hasRateLimiting)}}
61
- @Injectable()
62
- class GraphqlThrottlerGuard extends ThrottlerGuard {
63
- protected getRequestResponse(context: ExecutionContext) {
64
- if (context.getType<string>() !== "graphql") return super.getRequestResponse(context);
65
- const gqlContext = GqlExecutionContext.create(context).getContext();
66
- return { req: gqlContext.req, res: { header: () => undefined } };
67
- }
68
-
69
- protected getTracker(request: Record<string, unknown>): Promise<string> {
70
- const socket = request.socket as { remoteAddress?: string } | undefined;
71
- const ip = request.ip;
72
- return Promise.resolve(typeof ip === "string" ? ip : socket?.remoteAddress ?? "unknown");
73
- }
74
- }
75
- {{/if}}
76
-
77
60
  @Module({
78
61
  {{#if hasRest}}
79
62
  controllers: [
@@ -86,6 +69,7 @@ class GraphqlThrottlerGuard extends ThrottlerGuard {
86
69
  {{#if hasPostgres}}postgresUrl: env.POSTGRES_URL,{{/if}}
87
70
  {{#if hasMysql}}mysqlUrl: env.MYSQL_URL,{{/if}}
88
71
  {{#if hasSqlite}}sqlitePath: env.SQLITE_PATH,{{/if}}
72
+ {{#if hasSqlserver}}sqlserver: { server: env.SQLSERVER_HOST, port: env.SQLSERVER_PORT, database: env.SQLSERVER_DATABASE, user: env.SQLSERVER_USER, password: env.SQLSERVER_PASSWORD, options: { encrypt: env.SQLSERVER_ENCRYPT, trustServerCertificate: env.SQLSERVER_TRUST_SERVER_CERTIFICATE } },{{/if}}
89
73
  {{#if hasMongodb}}mongodbUrl: env.MONGODB_URL,{{/if}}
90
74
  }),
91
75
  {{/if}}
@@ -1,4 +1,4 @@
1
- {{#if (or hasPostgres hasMysql hasSqlite)}}
1
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
2
2
  export interface ISqlAdapter<TClient = unknown> {
3
3
  query: <T = unknown>(sql: string, params?: unknown[]) => Promise<T[]>;
4
4
  execute: (sql: string, params?: unknown[]) => Promise<unknown>;
@@ -1,5 +1,7 @@
1
- import { Injectable, Inject, type OnModuleInit, type OnModuleDestroy } from "@nestjs/common";
2
- import { MongoClient, type Collection, type Db, type Document } from "mongodb";
1
+ import { Injectable, Inject } from "@nestjs/common";
2
+ import type { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
3
+ import { MongoClient } from "mongodb";
4
+ import type { Collection, Db, Document } from "mongodb";
3
5
  import type { IMongoAdapter } from "../interfaces/db-adapter.interface.js";
4
6
 
5
7
  @Injectable()
@@ -27,7 +27,7 @@ import { PrismaClient } from '@prisma/client';
27
27
  import { PrismaClient } from '../database/orm/prisma/generated/client.js';
28
28
  {{/if}}
29
29
  {{/if}}
30
- {{#if (or hasPostgres hasMysql hasSqlite)}}
30
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
31
31
  import { PrismaSqlAdapter } from './adapters/sql/prisma-sql.adapter.js';
32
32
  {{/if}}
33
33
  {{#if hasPostgres}}
@@ -38,6 +38,9 @@ import { PrismaMariaDb } from '@prisma/adapter-mariadb';
38
38
  {{/if}}
39
39
  {{#if hasSqlite}}
40
40
  import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';
41
+ {{/if}}
42
+ {{#if hasSqlserver}}
43
+ import { PrismaMssql } from '@prisma/adapter-mssql';
41
44
  {{/if}}
42
45
  {{#if hasMongodb}}
43
46
  import { PrismaMongoAdapter } from './adapters/mongo/prisma-mongo.adapter.js';
@@ -63,6 +66,10 @@ import mysql from 'mysql2/promise';
63
66
  import { drizzle } from 'drizzle-orm/better-sqlite3';
64
67
  import Database from 'better-sqlite3';
65
68
  {{/if}}
69
+ {{#if hasSqlserver}}
70
+ import { drizzle } from 'drizzle-orm/node-mssql';
71
+ import sql from 'mssql';
72
+ {{/if}}
66
73
  {{/if}}
67
74
 
68
75
  {{#if useTypeorm}}
@@ -99,6 +106,9 @@ export interface DbModuleOptions {
99
106
  {{#if hasSqlite}}
100
107
  sqlitePath?: string;
101
108
  {{/if}}
109
+ {{#if hasSqlserver}}
110
+ sqlserver?: { server: string; port: number; database: string; user: string; password: string; options: { encrypt: boolean; trustServerCertificate: boolean } };
111
+ {{/if}}
102
112
  {{#if hasMongodb}}
103
113
  mongodbUrl?: string;
104
114
  {{/if}}
@@ -119,7 +129,7 @@ export class DbModule {
119
129
  const providers = initialProviders;
120
130
 
121
131
  {{#if usePrisma}}
122
- {{#if (or hasPostgres hasMysql hasSqlite)}}
132
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
123
133
  {{#if hasPostgres}}
124
134
  const sqlAdapter = new PrismaPg({
125
135
  connectionString: options.postgresUrl,
@@ -137,6 +147,13 @@ export class DbModule {
137
147
  const sqlAdapter = new PrismaBetterSqlite3({
138
148
  url: options.sqlitePath || ':memory:',
139
149
  });
150
+ const prismaSql = new PrismaClient({ adapter: sqlAdapter });
151
+ {{/if}}
152
+ {{#if hasSqlserver}}
153
+ if (!options.sqlserver) {
154
+ throw new Error("SQL Server connection settings are required to initialize Prisma");
155
+ }
156
+ const sqlAdapter = new PrismaMssql(options.sqlserver);
140
157
  const prismaSql = new PrismaClient({ adapter: sqlAdapter });
141
158
  {{/if}}
142
159
  providers.push({
@@ -171,13 +188,23 @@ export class DbModule {
171
188
  {{#if useDrizzle}}
172
189
  providers.push({
173
190
  provide: 'DRIZZLE_CLIENT',
174
- useFactory: () => {
191
+ useFactory: {{#if hasSqlserver}}async {{/if}}() => {
175
192
  {{#if hasPostgres}}
176
193
  const client = new Pool({ connectionString: options.postgresUrl });
177
194
  return drizzle({ client, schema });
178
195
  {{else if hasMysql}}
179
196
  const client = mysql.createPool({ uri: options.mysqlUrl });
180
197
  return drizzle(client, { schema, mode: "default" });
198
+ {{else if hasSqlserver}}
199
+ const client = await new sql.ConnectionPool({
200
+ server: options.sqlserver?.server ?? 'localhost',
201
+ port: options.sqlserver?.port ?? 1433,
202
+ database: options.sqlserver?.database ?? 'app_db',
203
+ user: options.sqlserver?.user ?? 'sa',
204
+ password: options.sqlserver?.password ?? 'DevOnly_Strong_Password_2026!X9',
205
+ options: options.sqlserver?.options ?? { encrypt: true, trustServerCertificate: true },
206
+ }).connect();
207
+ return drizzle({ client, schema });
181
208
  {{else}}
182
209
  const client = new Database(options.sqlitePath || ':memory:');
183
210
  return drizzle({ client, schema });
@@ -18,7 +18,7 @@ export class DbService {
18
18
  }
19
19
  {{else}}
20
20
  import { Injectable, Inject } from '@nestjs/common';
21
- {{#if (or hasPostgres hasMysql hasSqlite)}}
21
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
22
22
  import type { ISqlAdapter } from './adapters/interfaces/db-adapter.interface.js';
23
23
  {{/if}}
24
24
  {{#if hasMongodb}}
@@ -49,6 +49,9 @@ import type { MySql2Database } from 'drizzle-orm/mysql2';
49
49
  {{#if hasSqlite}}
50
50
  import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
51
51
  {{/if}}
52
+ {{#if hasSqlserver}}
53
+ import type { NodeMsSqlDatabase } from 'drizzle-orm/node-mssql';
54
+ {{/if}}
52
55
  {{#if isMonorepo}}
53
56
  import type * as schema from './database/orm/drizzle/schema.js';
54
57
  {{else}}
@@ -58,13 +61,13 @@ import type * as schema from './orm/drizzle/schema.js';
58
61
  {{#if useTypeorm}}
59
62
  import type { DataSource } from 'typeorm';
60
63
  {{/if}}
61
- {{#if (or hasPostgres hasMysql hasSqlite)}}
64
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
62
65
  {{#if usePrisma}}
63
66
  type SqlClient = PrismaClient;
64
67
  {{else if useTypeorm}}
65
68
  type SqlClient = DataSource;
66
69
  {{else if useDrizzle}}
67
- type DrizzleClient = {{#if hasPostgres}}NodePgDatabase{{/if}}{{#if hasMysql}}MySql2Database{{/if}}{{#if hasSqlite}}BetterSQLite3Database{{/if}}<typeof schema>;
70
+ type DrizzleClient = {{#if hasPostgres}}NodePgDatabase{{/if}}{{#if hasMysql}}MySql2Database{{/if}}{{#if hasSqlite}}BetterSQLite3Database{{/if}}{{#if hasSqlserver}}NodeMsSqlDatabase<typeof schema>{{/if}}{{#unless hasSqlserver}}<typeof schema>{{/unless}};
68
71
  type SqlClient = DrizzleClient;
69
72
  {{else if hasPostgres}}
70
73
  type SqlClient = import('pg').Pool;
@@ -91,7 +94,7 @@ import { db as prismaDb } from './prisma/db.js';
91
94
  @Injectable()
92
95
  export class DbService {
93
96
  constructor(
94
- {{#if (or hasPostgres hasMysql hasSqlite)}}
97
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
95
98
  @Inject('SQL_ADAPTER') readonly sql: ISqlAdapter<SqlClient>{{#if hasMongodb}},{{/if}}
96
99
  {{/if}}
97
100
  {{#if hasMongodb}}
@@ -101,7 +104,7 @@ export class DbService {
101
104
 
102
105
  get raw() {
103
106
  return {
104
- {{#if (or hasPostgres hasMysql hasSqlite)}}
107
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
105
108
  sql: this.sql.getClient(),
106
109
  {{/if}}
107
110
  {{#if hasMongodb}}
@@ -114,7 +117,7 @@ export class DbService {
114
117
  return prismaDb;
115
118
  }
116
119
  {{else if usePrisma}}
117
- {{#if (or hasPostgres hasMysql hasSqlite)}}
120
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
118
121
  get prisma(): PrismaClient {
119
122
  return this.sql.getClient();
120
123
  }
@@ -125,14 +128,14 @@ export class DbService {
125
128
  {{/if}}
126
129
  {{/if}}
127
130
  {{#if useDrizzle}}
128
- {{#if (or hasPostgres hasMysql hasSqlite)}}
131
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
129
132
  get drizzle(): DrizzleClient {
130
133
  return this.sql.getClient();
131
134
  }
132
135
  {{/if}}
133
136
  {{/if}}
134
137
  {{#if useTypeorm}}
135
- {{#if (or hasPostgres hasMysql hasSqlite)}}
138
+ {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
136
139
  get typeorm(): DataSource {
137
140
  return this.sql.getClient();
138
141
  }
@@ -12,7 +12,7 @@ import {
12
12
 
13
13
  @Entity("todos")
14
14
  export class Todo {
15
- @PrimaryGeneratedColumn("uuid")
15
+ @PrimaryGeneratedColumn("{{#if hasSqlserver}}increment{{else}}uuid{{/if}}")
16
16
  id: string;
17
17
 
18
18
  @Column({ type: "varchar", length: 255, nullable: true })
@@ -21,7 +21,7 @@ export class Todo {
21
21
  @Column({ type: "varchar", length: 255 })
22
22
  title: string;
23
23
 
24
- @Column({ default: false, name: "is_completed", type: "boolean" })
24
+ @Column({ default: false, name: "is_completed", type: "{{#if hasSqlserver}}bit{{else}}boolean{{/if}}" })
25
25
  completed: boolean;
26
26
 
27
27
  {{#if hasAuth}}
@@ -9,6 +9,7 @@ export type { PrismaClient } from '@prisma/client';
9
9
  export type { Todo } from '@prisma/client';
10
10
  {{/if}}
11
11
  {{else}}
12
+ {{#unless isPrisma8}}
12
13
  {{#if isMonorepo}}
13
14
  export type { PrismaClient } from './database/orm/prisma/generated/client.js';
14
15
  {{#if hasTodoExample}}
@@ -20,6 +21,7 @@ export type { PrismaClient } from './orm/prisma/generated/client.js';
20
21
  export type { Todo } from './orm/prisma/generated/client.js';
21
22
  {{/if}}
22
23
  {{/if}}
24
+ {{/unless}}
23
25
  {{/if}}
24
26
  {{/if}}
25
27
  export * from './adapters/interfaces/db-adapter.interface.js';
@@ -1,12 +1,22 @@
1
1
  // oxlint-disable typescript/no-explicit-any
2
+ {{#if hasPostgres}}
3
+ import { {{#if hasTodoExample}}boolean, {{/if}}pgTable{{#if (or (not isPassportAuth) hasTodoExample)}}, serial{{/if}}, text{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}, timestamp{{/if}} } from 'drizzle-orm/pg-core';
4
+ {{/if}}
5
+ {{#if hasMysql}}
6
+ import { {{#if hasTodoExample}}boolean, {{/if}}mysqlTable{{#if (or (not isPassportAuth) hasTodoExample)}}, int{{/if}}, text{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}, timestamp, varchar{{/if}} } from 'drizzle-orm/mysql-core';
7
+ {{/if}}
8
+ {{#if hasSqlite}}
9
+ import { sqliteTable{{#if (or (not isPassportAuth) hasTodoExample)}}, integer{{/if}}, text } from 'drizzle-orm/sqlite-core';
10
+ {{/if}}
11
+ {{#if hasSqlserver}}
12
+ import { bit, int, mssqlTable, nvarchar } from 'drizzle-orm/mssql-core';
13
+ {{/if}}
2
14
  {{#if (eq auth "better-auth")}}
3
15
  // oxlint-disable-next-line oxc/no-barrel-file
4
16
  export * from "./auth-schema.js";
5
17
  {{/if}}
6
18
  {{#if hasPostgres}}
7
- import { {{#if hasTodoExample}}boolean, {{/if}}pgTable{{#if (or (not isPassportAuth) hasTodoExample)}}, serial{{/if}}, text, timestamp } from 'drizzle-orm/pg-core';
8
-
9
- {{#unless (eq auth "better-auth")}}
19
+ {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
10
20
  export const users = pgTable('users', {
11
21
  email: text('email').unique(),
12
22
  {{#if isPassportAuth}}
@@ -23,7 +33,7 @@ export const users = pgTable('users', {
23
33
  role: text('role').default('USER'),
24
34
  {{/if}}
25
35
  });
26
- {{/unless}}
36
+ {{/if}}
27
37
  {{#if hasTodoExample}}
28
38
  export const todos = pgTable('todos', {
29
39
  {{#if hasAuth}}
@@ -37,9 +47,7 @@ export const todos = pgTable('todos', {
37
47
  {{/if}}
38
48
  {{/if}}
39
49
  {{#if hasMysql}}
40
- import { {{#if hasTodoExample}}boolean, {{/if}}mysqlTable{{#if (or (not isPassportAuth) hasTodoExample)}}, int{{/if}}, text, timestamp, varchar } from 'drizzle-orm/mysql-core';
41
-
42
- {{#unless (eq auth "better-auth")}}
50
+ {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
43
51
  export const users = mysqlTable('users', {
44
52
  email: varchar('email', { length: 320 }).unique(),
45
53
  {{#if isPassportAuth}}
@@ -56,7 +64,7 @@ export const users = mysqlTable('users', {
56
64
  role: text('role').default('USER'),
57
65
  {{/if}}
58
66
  });
59
- {{/unless}}
67
+ {{/if}}
60
68
  {{#if hasTodoExample}}
61
69
  export const todos = mysqlTable('todos', {
62
70
  {{#if hasAuth}}
@@ -70,9 +78,7 @@ export const todos = mysqlTable('todos', {
70
78
  {{/if}}
71
79
  {{/if}}
72
80
  {{#if hasSqlite}}
73
- import { sqliteTable{{#if (or (not isPassportAuth) hasTodoExample)}}, integer{{/if}}, text } from 'drizzle-orm/sqlite-core';
74
-
75
- {{#unless (eq auth "better-auth")}}
81
+ {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
76
82
  export const users = sqliteTable('users', {
77
83
  email: text('email').unique(),
78
84
  {{#if isPassportAuth}}
@@ -88,7 +94,7 @@ export const users = sqliteTable('users', {
88
94
  role: text('role').default('USER'),
89
95
  {{/if}}
90
96
  });
91
- {{/unless}}
97
+ {{/if}}
92
98
  {{#if hasTodoExample}}
93
99
  export const todos = sqliteTable('todos', {
94
100
  {{#if hasAuth}}
@@ -101,3 +107,33 @@ export const todos = sqliteTable('todos', {
101
107
  });
102
108
  {{/if}}
103
109
  {{/if}}
110
+ {{#if hasSqlserver}}
111
+ {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
112
+ export const users = mssqlTable('users', {
113
+ email: nvarchar('email', { length: 320 }).unique(),
114
+ {{#if isPassportAuth}}
115
+ id: nvarchar('id', { length: 255 }).primaryKey(),
116
+ {{else}}
117
+ id: int('id').identity().primaryKey(),
118
+ {{/if}}
119
+ name: nvarchar('name', { length: 255 }),
120
+ {{#if isPassportAuth}}
121
+ password: nvarchar('password', { length: 255 }).notNull(),
122
+ firstname: nvarchar('firstname', { length: 255 }),
123
+ lastname: nvarchar('lastname', { length: 255 }),
124
+ role: nvarchar('role', { length: 32 }).default('USER'),
125
+ {{/if}}
126
+ });
127
+ {{/if}}
128
+ {{#if hasTodoExample}}
129
+ export const todos = mssqlTable('todos', {
130
+ {{#if hasAuth}}
131
+ authorId: nvarchar('author_id', { length: 255 }),
132
+ {{/if}}
133
+ completed: bit('completed').notNull().default(false),
134
+ description: nvarchar('description', { length: 'max' }),
135
+ id: int('id').identity().primaryKey(),
136
+ title: nvarchar('title', { length: 255 }).notNull(),
137
+ });
138
+ {{/if}}
139
+ {{/if}}
@@ -1,5 +1,6 @@
1
1
  // oxlint-disable typescript/no-non-null-assertion
2
2
  import "reflect-metadata";
3
+ import "dotenv/config";
3
4
  {{#if isMonorepo}}
4
5
  import { env } from "@repo/env/api-gateway";
5
6
  {{else}}
@@ -16,27 +17,42 @@ import { UserEntity } from "../../../modules/users/entities/user.entity.js";
16
17
  import { AppEntity } from "../../entities/app.entity.js";
17
18
  {{/if}}
18
19
 
19
- const databaseUrl =
20
- env.{{#if hasPostgres}}POSTGRES_URL{{/if}}{{#if hasMysql}}MYSQL_URL{{/if}}{{#if hasSqlite}}SQLITE_PATH{{/if}};
21
-
22
- {{#unless hasSqlite}}
20
+ {{#unless (or hasSqlite hasSqlserver)}}
21
+ const databaseUrl = env.{{#if hasPostgres}}POSTGRES_URL{{/if}}{{#if hasMysql}}MYSQL_URL{{/if}};
23
22
  const parsedUrl = new URL(databaseUrl);
24
23
  {{/unless}}
25
24
 
26
25
 
27
26
  export const AppDataSource = new DataSource({
28
27
  {{#if hasSqlite}}
29
- database: databaseUrl,
28
+ database: env.SQLITE_PATH,
29
+ {{/if}}
30
+ {{#if hasSqlserver}}
31
+ database: env.SQLSERVER_DATABASE,
30
32
  {{/if}}
31
33
  // Register entities explicitly for consistent metadata in Node and test runners.
32
34
  entities: [{{#if hasTodoExample}}Todo{{/if}}{{#if (and hasTodoExample (and useTypeorm isPassportAuth))}}, {{/if}}{{#if (and useTypeorm isPassportAuth)}}UserEntity{{/if}}{{#if (eq auth "better-auth:stateless")}}{{#if (or hasTodoExample (and useTypeorm isPassportAuth))}}, {{/if}}AppEntity{{/if}}],
33
- migrations: [{{#if isMonorepo}}"migrations/*.{ts,js}"{{else}}"migrations/*.{ts,js}"{{/if}}],
34
- synchronize: env.NODE_ENV === "development",
35
- type: "{{#if hasPostgres}}postgres{{/if}}{{#if hasMysql}}mysql{{/if}}{{#if hasSqlite}}better-sqlite3{{/if}}",
36
- {{#unless hasSqlite}}
35
+ {{#if hasSqlserver}}
36
+ host: env.SQLSERVER_HOST,
37
+ {{/if}}
38
+ migrations: [
39
+ "dist/database/orm/typeorm/migrations/*.js",
40
+ "src/database/orm/typeorm/migrations/*.js",
41
+ ],
42
+ {{#if hasSqlserver}}
43
+ options: { encrypt: env.SQLSERVER_ENCRYPT, trustServerCertificate: env.SQLSERVER_TRUST_SERVER_CERTIFICATE },
44
+ password: env.SQLSERVER_PASSWORD,
45
+ port: env.SQLSERVER_PORT,
46
+ {{/if}}
47
+ synchronize: false,
48
+ type: "{{#if hasPostgres}}postgres{{/if}}{{#if hasMysql}}mysql{{/if}}{{#if hasSqlite}}better-sqlite3{{/if}}{{#if hasSqlserver}}mssql{{/if}}",
49
+ {{#if hasSqlserver}}
50
+ username: env.SQLSERVER_USER,
51
+ {{/if}}
52
+ {{#unless (or hasSqlite hasSqlserver)}}
37
53
  url: databaseUrl,
38
54
  {{/unless}}
39
- {{#unless hasSqlite}}
55
+ {{#unless (or hasSqlite hasSqlserver)}}
40
56
  ...(parsedUrl.username ? { username: decodeURIComponent(parsedUrl.username) } : {}),
41
57
  ...(parsedUrl.password ? { password: decodeURIComponent(parsedUrl.password) } : {}),
42
58
  {{/unless}}
@@ -0,0 +1,22 @@
1
+ import { Injectable, type ExecutionContext } from "@nestjs/common";
2
+ import { GqlExecutionContext } from "@nestjs/graphql";
3
+ import { ThrottlerGuard } from "@nestjs/throttler";
4
+
5
+ @Injectable()
6
+ export class GraphqlThrottlerGuard extends ThrottlerGuard {
7
+ protected getRequestResponse(context: ExecutionContext) {
8
+ if (context.getType<string>() !== "graphql") {
9
+ return super.getRequestResponse(context);
10
+ }
11
+ const gqlContext = GqlExecutionContext.create(context).getContext();
12
+ return { req: gqlContext.req, res: { header: () => true } };
13
+ }
14
+
15
+ protected getTracker(request: Record<string, unknown>): Promise<string> {
16
+ const socket = request.socket as { remoteAddress?: string } | undefined;
17
+ const ip = request.ip;
18
+ return Promise.resolve(
19
+ typeof ip === "string" ? ip : (socket?.remoteAddress ?? "unknown")
20
+ );
21
+ }
22
+ }