@nest-arch/tui 0.4.6 → 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 (42) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/constants-DX5wiqrq.js +1 -0
  3. package/dist/index.js +145 -133
  4. package/dist/templates/addons/husky/pre-commit.hbs +1 -1
  5. package/dist/templates/base/.env.example.hbs +7 -2
  6. package/dist/templates/base/.env.hbs +7 -2
  7. package/dist/templates/base/README.md.hbs +11 -2
  8. package/dist/templates/base/pnpm-workspace.yaml.hbs +9 -4
  9. package/dist/templates/base/prisma.config.ts.hbs +5 -0
  10. package/dist/templates/base/src/app.module.ts.hbs +5 -22
  11. package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +4 -2
  12. package/dist/templates/base/src/database/db.module.ts.hbs +15 -1
  13. package/dist/templates/base/src/database/db.service.ts.hbs +6 -3
  14. package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +2 -2
  15. package/dist/templates/base/src/database/index.ts.hbs +2 -0
  16. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +48 -12
  17. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +26 -10
  18. package/dist/templates/base/src/libs/middlewares/graphql-throttler.guard.ts.hbs +22 -0
  19. package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +2 -2
  20. package/dist/templates/base/src/main.ts.hbs +2 -2
  21. package/dist/templates/base/test/app.e2e-spec.ts.hbs +368 -196
  22. package/dist/templates/extras/docker/Dockerfile.hbs +8 -1
  23. package/dist/templates/extras/health/src/health/health.controller.ts.hbs +7 -1
  24. package/dist/templates/extras/swagger/create-docs.ts.hbs +6 -1
  25. package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +1 -1
  26. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +1 -1
  27. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +80 -7
  28. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +181 -25
  29. package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +2 -2
  30. package/dist/templates/monorepo/root/README.md.hbs +8 -13
  31. package/dist/templates/single-project/architecture/nest-api/.env.hbs +6 -2
  32. package/dist/templates/single-project/architecture/nest-api/pnpm-workspace.yaml.hbs +6 -0
  33. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +2 -2
  34. package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +2 -2
  35. package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +6 -0
  36. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +2 -2
  37. package/dist/templates/single-project/architecture/nest-microservice/src/app.module.ts.hbs +28 -1
  38. package/dist/templates/single-project/architecture/nest-microservice/src/http-throttler.guard.ts.hbs +13 -0
  39. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +2 -2
  40. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +35 -0
  41. package/package.json +5 -5
  42. package/dist/constants-DVhgS48N.js +0 -1
@@ -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
 
@@ -35,7 +35,12 @@ MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
35
35
  GRAPHQL_PLAYGROUND=true
36
36
 
37
37
  {{/if}}
38
- {{#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")}}
39
44
  # JWT
40
45
  JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
41
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}}
@@ -26,7 +26,12 @@ SQLSERVER_TRUST_SERVER_CERTIFICATE=true
26
26
  {{#if hasMongodb}}
27
27
  MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
28
28
  {{/if}}
29
- {{#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")}}
30
35
  JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
31
36
  {{#if hasDatabase}}
32
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
 
@@ -98,9 +102,14 @@ This project uses **Better Auth** with database-backed sessions. Set `BETTER_AUT
98
102
 
99
103
  Run `auth:generate` with your package manager. The script uses the installed CLI version.
100
104
  {{#if usePrisma}}
101
- 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.
102
111
  {{else if useDrizzle}}
103
- 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.
104
113
  {{else}}
105
114
  Then run `auth:migrate` and `build`.
106
115
  {{/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,6 +27,11 @@ 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}}
@@ -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: [
@@ -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()
@@ -66,6 +66,10 @@ import mysql from 'mysql2/promise';
66
66
  import { drizzle } from 'drizzle-orm/better-sqlite3';
67
67
  import Database from 'better-sqlite3';
68
68
  {{/if}}
69
+ {{#if hasSqlserver}}
70
+ import { drizzle } from 'drizzle-orm/node-mssql';
71
+ import sql from 'mssql';
72
+ {{/if}}
69
73
  {{/if}}
70
74
 
71
75
  {{#if useTypeorm}}
@@ -184,13 +188,23 @@ export class DbModule {
184
188
  {{#if useDrizzle}}
185
189
  providers.push({
186
190
  provide: 'DRIZZLE_CLIENT',
187
- useFactory: () => {
191
+ useFactory: {{#if hasSqlserver}}async {{/if}}() => {
188
192
  {{#if hasPostgres}}
189
193
  const client = new Pool({ connectionString: options.postgresUrl });
190
194
  return drizzle({ client, schema });
191
195
  {{else if hasMysql}}
192
196
  const client = mysql.createPool({ uri: options.mysqlUrl });
193
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 });
194
208
  {{else}}
195
209
  const client = new Database(options.sqlitePath || ':memory:');
196
210
  return drizzle({ client, schema });
@@ -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}}
@@ -64,7 +67,7 @@ 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;
@@ -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
+ }
@@ -1,10 +1,10 @@
1
1
  import { ContextOptions } from "nestjs-trpc";
2
- {{#if (eq auth "better-auth")}}
2
+ {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
3
3
  import type { UserSession } from "../../auth/auth.js";
4
4
  {{else if (eq auth "passport")}}
5
5
  import type { AuthService } from "../../auth/auth.service.js";
6
6
  {{/if}}
7
7
 
8
8
  export type InnerContext = {
9
- session?: {{#if (eq auth "better-auth")}}UserSession{{else if (eq auth "passport")}}{ user: NonNullable<Awaited<ReturnType<AuthService[{{#if (and usePrisma (not hasMongodb))}}"validate"{{else}}"validateUser"{{/if}}]>>> }{{else}}unknown{{/if}};
9
+ session?: {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}UserSession{{else if (eq auth "passport")}}{ user: NonNullable<Awaited<ReturnType<AuthService[{{#if (and usePrisma (not hasMongodb))}}"validate"{{else}}"validateUser"{{/if}}]>>> }{{else}}unknown{{/if}};
10
10
  } & ContextOptions;
@@ -70,8 +70,8 @@ async function bootstrap() {
70
70
  {{/if}}
71
71
  {{#if (includes apis "trpc")}}
72
72
  trpc: {
73
- routerFilePath: "./src/libs/trpc/@generated/server.ts",
74
73
  pathPrefix: "/trpc",
74
+ routerFilePath: "./{{trpcGeneratePath}}/server.ts",
75
75
  },
76
76
  {{/if}}
77
77
  zod: {
@@ -98,8 +98,8 @@ async function bootstrap() {
98
98
  {{/if}}
99
99
  {{#if (includes apis "trpc")}}
100
100
  trpc: {
101
- routerFilePath: "./src/trpc/router.ts",
102
101
  pathPrefix: "/trpc",
102
+ routerFilePath: "./{{trpcGeneratePath}}/server.ts",
103
103
  },
104
104
  {{/if}}
105
105
  zod: {