@nest-arch/tui 0.5.0 → 0.5.1

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 (35) hide show
  1. package/README.md +31 -0
  2. package/dist/cli.js +2 -1
  3. package/dist/{constants-DX5wiqrq.js → constants-CUTbfu9C.js} +1 -1
  4. package/dist/index.js +6 -417
  5. package/dist/nonInteractive-C1KZ0b_6.js +5 -0
  6. package/dist/nonInteractiveOptions-CIaoLAWg.js +24 -0
  7. package/dist/projectGeneratorService-CeWJK-HA.js +420 -0
  8. package/dist/projectName-CcE90t2V.js +1 -0
  9. package/dist/templates/base/.env.example.hbs +23 -0
  10. package/dist/templates/base/prisma.config.ts.hbs +6 -6
  11. package/dist/templates/base/src/app.controller.spec.ts.hbs +6 -0
  12. package/dist/templates/base/src/app.module.ts.hbs +1 -1
  13. package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +5 -5
  14. package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +2 -2
  15. package/dist/templates/base/src/database/adapters/mongo/typeorm-mongo.adapter.ts.hbs +27 -0
  16. package/dist/templates/base/src/database/db.module.ts.hbs +22 -16
  17. package/dist/templates/base/src/database/db.service.ts.hbs +7 -2
  18. package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +15 -3
  19. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +38 -14
  20. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -6
  21. package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +2 -1
  22. package/dist/templates/base/test/app.e2e-spec.ts.hbs +14 -10
  23. package/dist/templates/extras/docker/Dockerfile.hbs +2 -2
  24. package/dist/templates/extras/health/src/health/health.controller.ts.hbs +5 -0
  25. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +4 -4
  26. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +135 -11
  27. package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +24 -1
  28. package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
  29. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +24 -0
  30. package/dist/templates/single-project/architecture/nest-microservice/src/app.module.ts.hbs +33 -0
  31. package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +16 -4
  32. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +25 -6
  33. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +7 -122
  34. package/dist/templates/single-project/shared/config/env.ts.hbs +14 -14
  35. package/package.json +2 -1
@@ -0,0 +1 @@
1
+ var a=Object.defineProperty;var r=(e,t)=>a(e,"name",{value:t,configurable:!0});const s="my-nest-app",m=64,n=/^[a-z][a-z0-9-]*$/u;function E(e){return e.trim()||s}r(E,"resolveProjectName");function o(e){const t=e.trim();return t?t.length>64?"Project name must be at most 64 characters":n.test(t)?null:"Use lowercase letters, numbers, and hyphens; must start with a letter":null}r(o,"validateProjectName");export{s as D,m as M,n as P,E as r,o as v};
@@ -5,6 +5,29 @@ 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
 
8
+ {{#if (eq architecture "nest-microservice")}}
9
+ # Microservice transport
10
+ MICROSERVICE_PORT=3001
11
+ {{/if}}
12
+ {{#if (includes microservices "redis")}}
13
+ REDIS_URL=redis://127.0.0.1:6379
14
+ {{/if}}
15
+ {{#if (includes microservices "mqtt")}}
16
+ MQTT_URL=mqtt://127.0.0.1:1883
17
+ {{/if}}
18
+ {{#if (includes microservices "nats")}}
19
+ NATS_URL=nats://127.0.0.1:4222
20
+ {{/if}}
21
+ {{#if (includes microservices "rabbitmq")}}
22
+ RABBITMQ_URL=amqp://127.0.0.1:5672
23
+ {{/if}}
24
+ {{#if (includes microservices "kafka")}}
25
+ KAFKA_BROKER=127.0.0.1:9092
26
+ {{/if}}
27
+ {{#if (includes microservices "grpc")}}
28
+ GRPC_URL=0.0.0.0:5000
29
+ {{/if}}
30
+
8
31
  {{#if hasDatabase}}
9
32
  # Database
10
33
  {{/if}}
@@ -26,12 +26,6 @@ export default definePrismaConfig({
26
26
  import { defineConfig } from "prisma/config";
27
27
 
28
28
  export default defineConfig({
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}}
35
29
  datasource: {
36
30
  {{#if hasPostgres}}url: env.POSTGRES_URL,{{/if}}
37
31
  {{#if hasMysql}}url: env.MYSQL_URL,{{/if}}
@@ -39,5 +33,11 @@ export default defineConfig({
39
33
  {{#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}}
40
34
  {{#if hasMongodb}}url: env.MONGODB_URL,{{/if}}
41
35
  },
36
+ {{#unless isMonorepo}}
37
+ migrations: {
38
+ path: "src/database/orm/prisma/migrations",
39
+ },
40
+ {{/unless}}
41
+ schema: "src/database/orm/prisma{{#unless isBetterAuth}}/schema.prisma{{/unless}}",
42
42
  });
43
43
  {{/if}}
@@ -7,6 +7,9 @@ import { AppController } from "./app.controller.js";
7
7
  import { AppService } from "./app.service.js";
8
8
  {{#if (or hasDatabase hasOrm)}}
9
9
  import { DbService } from "./database/db.service.js";
10
+ {{#if (and (eq architecture "nest-microservice") hasTodoExample)}}
11
+ import { TodoService } from "./api/todo/todo.service.js";
12
+ {{/if}}
10
13
  {{/if}}
11
14
 
12
15
 
@@ -21,6 +24,9 @@ describe(AppController, () => {
21
24
  AppService,
22
25
  {{#if (or hasDatabase hasOrm)}}
23
26
  { provide: DbService, useValue: {} },
27
+ {{#if (and (eq architecture "nest-microservice") hasTodoExample)}}
28
+ { provide: TodoService, useValue: {} },
29
+ {{/if}}
24
30
  {{/if}}
25
31
  ],
26
32
  }).compile();
@@ -69,7 +69,7 @@ import { AppService } from "./app.service.js";
69
69
  {{#if hasPostgres}}postgresUrl: env.POSTGRES_URL,{{/if}}
70
70
  {{#if hasMysql}}mysqlUrl: env.MYSQL_URL,{{/if}}
71
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}}
72
+ {{#if hasSqlserver}}sqlserver: { database: env.SQLSERVER_DATABASE, options: { encrypt: env.SQLSERVER_ENCRYPT, trustServerCertificate: env.SQLSERVER_TRUST_SERVER_CERTIFICATE }, password: env.SQLSERVER_PASSWORD, port: env.SQLSERVER_PORT, server: env.SQLSERVER_HOST, user: env.SQLSERVER_USER },{{/if}}
73
73
  {{#if hasMongodb}}mongodbUrl: env.MONGODB_URL,{{/if}}
74
74
  }),
75
75
  {{/if}}
@@ -1,3 +1,6 @@
1
+ {{#if (and hasMongodb (or useRawSql useTypeorm))}}
2
+ import type { Collection, Document } from "mongodb";
3
+ {{/if}}
1
4
  {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
2
5
  export interface ISqlAdapter<TClient = unknown> {
3
6
  query: <T = unknown>(sql: string, params?: unknown[]) => Promise<T[]>;
@@ -7,12 +10,9 @@ export interface ISqlAdapter<TClient = unknown> {
7
10
  {{/if}}
8
11
 
9
12
  {{#if hasMongodb}}
10
- {{#if useRawSql}}
11
- import type { Collection, Document } from "mongodb";
12
- {{/if}}
13
13
  export interface IMongoAdapter<TClient = unknown> {
14
- {{#if useRawSql}}
15
- collection: <T extends Document = Document>(name: string) => Collection<T>;
14
+ {{#if (or useRawSql useTypeorm)}}
15
+ collection: <T = unknown>(name: string) => Collection<T & Document>;
16
16
  {{else}}
17
17
  collection: <T = unknown>(name: string) => T;
18
18
  {{/if}}
@@ -7,8 +7,8 @@ import type { IMongoAdapter } from "../interfaces/db-adapter.interface.js";
7
7
  @Injectable()
8
8
  export class RawMongoAdapter implements IMongoAdapter<Db>, OnModuleInit, OnModuleDestroy {
9
9
  constructor(@Inject("MONGODB_CLIENT") private readonly client: MongoClient) {}
10
- collection<T extends Document = Document>(name: string): Collection<T> {
11
- return this.client.db().collection<T>(name);
10
+ collection<T = unknown>(name: string): Collection<T & Document> {
11
+ return this.client.db().collection<T & Document>(name);
12
12
  }
13
13
  getClient(): Db { return this.client.db(); }
14
14
  async onModuleInit(): Promise<void> {
@@ -0,0 +1,27 @@
1
+ import { Inject, Injectable } from "@nestjs/common";
2
+ import type { OnModuleDestroy } from "@nestjs/common";
3
+ import type { DataSource } from "typeorm";
4
+ import type { Collection, Document } from "mongodb";
5
+ import type { IMongoAdapter } from "../interfaces/db-adapter.interface.js";
6
+
7
+ @Injectable()
8
+ export class TypeormMongoAdapter implements IMongoAdapter<DataSource>, OnModuleDestroy {
9
+ constructor(@Inject("TYPEORM_DATA_SOURCE") private readonly dataSource: DataSource) {}
10
+
11
+ collection<T = unknown>(name: string): Collection<T & Document> {
12
+ const databaseName = this.dataSource.options.database;
13
+ return this.dataSource.mongoManager.mongoQueryRunner.databaseConnection
14
+ .db(typeof databaseName === "string" ? databaseName : undefined)
15
+ .collection(name) as unknown as Collection<T & Document>;
16
+ }
17
+
18
+ getClient(): DataSource {
19
+ return this.dataSource;
20
+ }
21
+
22
+ async onModuleDestroy(): Promise<void> {
23
+ if (this.dataSource.isInitialized) {
24
+ await this.dataSource.destroy();
25
+ }
26
+ }
27
+ }
@@ -73,7 +73,11 @@ import sql from 'mssql';
73
73
  {{/if}}
74
74
 
75
75
  {{#if useTypeorm}}
76
+ {{#if hasMongodb}}
77
+ import { TypeormMongoAdapter } from './adapters/mongo/typeorm-mongo.adapter.js';
78
+ {{else}}
76
79
  import { TypeormSqlAdapter } from './adapters/sql/typeorm-sql.adapter.js';
80
+ {{/if}}
77
81
  import { AppDataSource } from "./orm/typeorm/data-source.js";
78
82
  {{/if}}
79
83
 
@@ -159,8 +163,7 @@ export class DbModule {
159
163
  providers.push({
160
164
  provide: 'PRISMA_SQL_CLIENT',
161
165
  useValue: prismaSql,
162
- });
163
- providers.push({
166
+ }, {
164
167
  provide: 'SQL_ADAPTER',
165
168
  useClass: PrismaSqlAdapter,
166
169
  });
@@ -177,8 +180,7 @@ export class DbModule {
177
180
  providers.push({
178
181
  provide: 'PRISMA_MONGO_CLIENT',
179
182
  useValue: prismaMongo,
180
- });
181
- providers.push({
183
+ }, {
182
184
  provide: 'MONGO_ADAPTER',
183
185
  useClass: PrismaMongoAdapter,
184
186
  });
@@ -194,15 +196,15 @@ export class DbModule {
194
196
  return drizzle({ client, schema });
195
197
  {{else if hasMysql}}
196
198
  const client = mysql.createPool({ uri: options.mysqlUrl });
197
- return drizzle(client, { schema, mode: "default" });
199
+ return drizzle(client, { mode: "default", schema });
198
200
  {{else if hasSqlserver}}
199
201
  const client = await new sql.ConnectionPool({
200
- server: options.sqlserver?.server ?? 'localhost',
201
- port: options.sqlserver?.port ?? 1433,
202
202
  database: options.sqlserver?.database ?? 'app_db',
203
- user: options.sqlserver?.user ?? 'sa',
204
- password: options.sqlserver?.password ?? 'DevOnly_Strong_Password_2026!X9',
205
203
  options: options.sqlserver?.options ?? { encrypt: true, trustServerCertificate: true },
204
+ password: options.sqlserver?.password ?? 'DevOnly_Strong_Password_2026!X9',
205
+ port: options.sqlserver?.port ?? 1433,
206
+ server: options.sqlserver?.server ?? 'localhost',
207
+ user: options.sqlserver?.user ?? 'sa',
206
208
  }).connect();
207
209
  return drizzle({ client, schema });
208
210
  {{else}}
@@ -210,18 +212,21 @@ export class DbModule {
210
212
  return drizzle({ client, schema });
211
213
  {{/if}}
212
214
  },
213
- });
214
- providers.push({ provide: 'SQL_ADAPTER', useClass: DrizzleSqlAdapter });
215
+ }, { provide: 'SQL_ADAPTER', useClass: DrizzleSqlAdapter });
215
216
  {{/if}}
216
217
 
217
218
  {{#if useTypeorm}}
218
219
  providers.push({
219
220
  provide: 'TYPEORM_DATA_SOURCE',
220
221
  useFactory: () => AppDataSource.initialize(),
221
- });
222
- providers.push({
222
+ }, {
223
+ {{#if hasMongodb}}
224
+ provide: 'MONGO_ADAPTER',
225
+ useClass: TypeormMongoAdapter,
226
+ {{else}}
223
227
  provide: 'SQL_ADAPTER',
224
228
  useClass: TypeormSqlAdapter,
229
+ {{/if}}
225
230
  });
226
231
  {{/if}}
227
232
 
@@ -260,12 +265,13 @@ export class DbModule {
260
265
  {{/if}}
261
266
 
262
267
  {{#if hasMongodb}}
263
- if (!options.mongodbUrl) throw new Error("A MongoDB URL is required");
268
+ if (!options.mongodbUrl) {
269
+ throw new Error("A MongoDB URL is required");
270
+ }
264
271
  providers.push({
265
272
  provide: 'MONGODB_CLIENT',
266
273
  useFactory: () => new MongoClient(options.mongodbUrl!, { serverSelectionTimeoutMS: 5000 }),
267
- });
268
- providers.push({
274
+ }, {
269
275
  provide: 'MONGO_ADAPTER',
270
276
  useClass: RawMongoAdapter,
271
277
  });
@@ -61,6 +61,7 @@ import type * as schema from './orm/drizzle/schema.js';
61
61
  {{#if useTypeorm}}
62
62
  import type { DataSource } from 'typeorm';
63
63
  {{/if}}
64
+
64
65
  {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
65
66
  {{#if usePrisma}}
66
67
  type SqlClient = PrismaClient;
@@ -78,6 +79,8 @@ type SqlClient = unknown;
78
79
  {{#if hasMongodb}}
79
80
  {{#if usePrisma}}
80
81
  type MongoClient = PrismaClient;
82
+ {{else if useTypeorm}}
83
+ type MongoClient = DataSource;
81
84
  {{else}}
82
85
  type MongoClient = import('mongodb').Db;
83
86
  {{/if}}
@@ -135,11 +138,13 @@ export class DbService {
135
138
  {{/if}}
136
139
  {{/if}}
137
140
  {{#if useTypeorm}}
138
- {{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
139
141
  get typeorm(): DataSource {
142
+ {{#if hasMongodb}}
143
+ return this.mongo.getClient();
144
+ {{else}}
140
145
  return this.sql.getClient();
146
+ {{/if}}
141
147
  }
142
- {{/if}}
143
148
  {{/if}}
144
149
  }
145
150
  {{/if}}
@@ -6,26 +6,38 @@ import {
6
6
  Column,
7
7
  CreateDateColumn,
8
8
  Entity,
9
+ {{#if hasMongodb}}
10
+ ObjectIdColumn,
11
+ {{else}}
9
12
  PrimaryGeneratedColumn,
13
+ {{/if}}
10
14
  UpdateDateColumn,
11
15
  } from "typeorm";
16
+ {{#if hasMongodb}}
17
+ import type { ObjectId } from "mongodb";
18
+ {{/if}}
12
19
 
13
20
  @Entity("todos")
14
21
  export class Todo {
22
+ {{#if hasMongodb}}
23
+ @ObjectIdColumn()
24
+ id: ObjectId;
25
+ {{else}}
15
26
  @PrimaryGeneratedColumn("{{#if hasSqlserver}}increment{{else}}uuid{{/if}}")
16
27
  id: string;
28
+ {{/if}}
17
29
 
18
- @Column({ type: "varchar", length: 255, nullable: true })
30
+ @Column({ length: 255, nullable: true, type: "varchar" })
19
31
  description?: string;
20
32
 
21
- @Column({ type: "varchar", length: 255 })
33
+ @Column({ length: 255, type: "varchar" })
22
34
  title: string;
23
35
 
24
36
  @Column({ default: false, name: "is_completed", type: "{{#if hasSqlserver}}bit{{else}}boolean{{/if}}" })
25
37
  completed: boolean;
26
38
 
27
39
  {{#if hasAuth}}
28
- @Column({ type: "varchar", nullable: true })
40
+ @Column({ nullable: true, type: "varchar" })
29
41
  authorId?: string;
30
42
  {{/if}}
31
43
 
@@ -1,15 +1,27 @@
1
1
  // oxlint-disable typescript/no-explicit-any
2
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';
3
+ {{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
4
+ import { {{#if hasTodoExample}}boolean, {{/if}}pgTable{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}, serial{{/if}}, text{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}, timestamp{{/if}} } from 'drizzle-orm/pg-core';
5
+
6
+ {{/if}}
4
7
  {{/if}}
5
8
  {{#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';
9
+ {{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
10
+ import { {{#if hasTodoExample}}boolean, {{/if}}mysqlTable{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}, int{{/if}}, text{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}, timestamp, varchar{{/if}} } from 'drizzle-orm/mysql-core';
11
+
12
+ {{/if}}
7
13
  {{/if}}
8
14
  {{#if hasSqlite}}
9
- import { sqliteTable{{#if (or (not isPassportAuth) hasTodoExample)}}, integer{{/if}}, text } from 'drizzle-orm/sqlite-core';
15
+ {{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
16
+ import { sqliteTable{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}, integer{{/if}}, text } from 'drizzle-orm/sqlite-core';
17
+
18
+ {{/if}}
10
19
  {{/if}}
11
20
  {{#if hasSqlserver}}
12
- import { bit, int, mssqlTable, nvarchar } from 'drizzle-orm/mssql-core';
21
+ {{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
22
+ import { {{#if hasTodoExample}}bit, {{/if}}{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}int, {{/if}}mssqlTable, nvarchar } from 'drizzle-orm/mssql-core';
23
+
24
+ {{/if}}
13
25
  {{/if}}
14
26
  {{#if (eq auth "better-auth")}}
15
27
  // oxlint-disable-next-line oxc/no-barrel-file
@@ -18,18 +30,22 @@ export * from "./auth-schema.js";
18
30
  {{#if hasPostgres}}
19
31
  {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
20
32
  export const users = pgTable('users', {
33
+ {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
34
+ createdAt: timestamp('created_at').defaultNow(),
35
+ {{/if}}
21
36
  email: text('email').unique(),
22
37
  {{#if isPassportAuth}}
38
+ firstname: text('firstname'),
23
39
  id: text('id').primaryKey(),
24
40
  {{else}}
25
41
  id: serial('id').primaryKey(),
42
+ {{/if}}
43
+ {{#if isPassportAuth}}
44
+ lastname: text('lastname'),
26
45
  {{/if}}
27
46
  name: text('name'),
28
- createdAt: timestamp('created_at').defaultNow(),
29
47
  {{#if isPassportAuth}}
30
48
  password: text('password').notNull(),
31
- firstname: text('firstname'),
32
- lastname: text('lastname'),
33
49
  role: text('role').default('USER'),
34
50
  {{/if}}
35
51
  });
@@ -49,18 +65,22 @@ export const todos = pgTable('todos', {
49
65
  {{#if hasMysql}}
50
66
  {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
51
67
  export const users = mysqlTable('users', {
68
+ {{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
69
+ createdAt: timestamp('created_at').defaultNow(),
70
+ {{/if}}
52
71
  email: varchar('email', { length: 320 }).unique(),
53
72
  {{#if isPassportAuth}}
73
+ firstname: text('firstname'),
54
74
  id: varchar('id', { length: 255 }).primaryKey(),
55
75
  {{else}}
56
76
  id: int('id').autoincrement().primaryKey(),
77
+ {{/if}}
78
+ {{#if isPassportAuth}}
79
+ lastname: text('lastname'),
57
80
  {{/if}}
58
81
  name: text('name'),
59
- createdAt: timestamp('created_at').defaultNow(),
60
82
  {{#if isPassportAuth}}
61
83
  password: text('password').notNull(),
62
- firstname: text('firstname'),
63
- lastname: text('lastname'),
64
84
  role: text('role').default('USER'),
65
85
  {{/if}}
66
86
  });
@@ -82,15 +102,17 @@ export const todos = mysqlTable('todos', {
82
102
  export const users = sqliteTable('users', {
83
103
  email: text('email').unique(),
84
104
  {{#if isPassportAuth}}
105
+ firstname: text('firstname'),
85
106
  id: text('id').primaryKey(),
86
107
  {{else}}
87
108
  id: integer('id').primaryKey({ autoIncrement: true }),
109
+ {{/if}}
110
+ {{#if isPassportAuth}}
111
+ lastname: text('lastname'),
88
112
  {{/if}}
89
113
  name: text('name'),
90
114
  {{#if isPassportAuth}}
91
115
  password: text('password').notNull(),
92
- firstname: text('firstname'),
93
- lastname: text('lastname'),
94
116
  role: text('role').default('USER'),
95
117
  {{/if}}
96
118
  });
@@ -112,15 +134,17 @@ export const todos = sqliteTable('todos', {
112
134
  export const users = mssqlTable('users', {
113
135
  email: nvarchar('email', { length: 320 }).unique(),
114
136
  {{#if isPassportAuth}}
137
+ firstname: nvarchar('firstname', { length: 255 }),
115
138
  id: nvarchar('id', { length: 255 }).primaryKey(),
116
139
  {{else}}
117
140
  id: int('id').identity().primaryKey(),
141
+ {{/if}}
142
+ {{#if isPassportAuth}}
143
+ lastname: nvarchar('lastname', { length: 255 }),
118
144
  {{/if}}
119
145
  name: nvarchar('name', { length: 255 }),
120
146
  {{#if isPassportAuth}}
121
147
  password: nvarchar('password', { length: 255 }).notNull(),
122
- firstname: nvarchar('firstname', { length: 255 }),
123
- lastname: nvarchar('lastname', { length: 255 }),
124
148
  role: nvarchar('role', { length: 32 }).default('USER'),
125
149
  {{/if}}
126
150
  });
@@ -7,7 +7,7 @@ import { env } from "@repo/env/api-gateway";
7
7
  import { env } from "../../../config/env.js";
8
8
  {{/if}}
9
9
  import { DataSource } from "typeorm";
10
- {{#if hasTodoExample}}
10
+ {{#if (or hasTodoExample hasMongodb)}}
11
11
  import { Todo } from "../../entities/todo.entity.js";
12
12
  {{/if}}
13
13
  {{#if (and useTypeorm isPassportAuth)}}
@@ -17,7 +17,7 @@ import { UserEntity } from "../../../modules/users/entities/user.entity.js";
17
17
  import { AppEntity } from "../../entities/app.entity.js";
18
18
  {{/if}}
19
19
 
20
- {{#unless (or hasSqlite hasSqlserver)}}
20
+ {{#unless (or hasSqlite hasSqlserver hasMongodb)}}
21
21
  const databaseUrl = env.{{#if hasPostgres}}POSTGRES_URL{{/if}}{{#if hasMysql}}MYSQL_URL{{/if}};
22
22
  const parsedUrl = new URL(databaseUrl);
23
23
  {{/unless}}
@@ -31,7 +31,10 @@ export const AppDataSource = new DataSource({
31
31
  database: env.SQLSERVER_DATABASE,
32
32
  {{/if}}
33
33
  // Register entities explicitly for consistent metadata in Node and test runners.
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}}],
34
+ entities: [{{#if (or hasTodoExample hasMongodb)}}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}}],
35
+ {{#if hasMongodb}}
36
+ extra: { serverSelectionTimeoutMS: 5000 },
37
+ {{/if}}
35
38
  {{#if hasSqlserver}}
36
39
  host: env.SQLSERVER_HOST,
37
40
  {{/if}}
@@ -45,14 +48,17 @@ export const AppDataSource = new DataSource({
45
48
  port: env.SQLSERVER_PORT,
46
49
  {{/if}}
47
50
  synchronize: false,
48
- type: "{{#if hasPostgres}}postgres{{/if}}{{#if hasMysql}}mysql{{/if}}{{#if hasSqlite}}better-sqlite3{{/if}}{{#if hasSqlserver}}mssql{{/if}}",
51
+ type: "{{#if hasPostgres}}postgres{{/if}}{{#if hasMysql}}mysql{{/if}}{{#if hasSqlite}}better-sqlite3{{/if}}{{#if hasSqlserver}}mssql{{/if}}{{#if hasMongodb}}mongodb{{/if}}",
52
+ {{#if hasMongodb}}
53
+ url: env.MONGODB_URL,
54
+ {{/if}}
49
55
  {{#if hasSqlserver}}
50
56
  username: env.SQLSERVER_USER,
51
57
  {{/if}}
52
- {{#unless (or hasSqlite hasSqlserver)}}
58
+ {{#unless (or hasSqlite hasSqlserver hasMongodb)}}
53
59
  url: databaseUrl,
54
60
  {{/unless}}
55
- {{#unless (or hasSqlite hasSqlserver)}}
61
+ {{#unless (or hasSqlite hasSqlserver hasMongodb)}}
56
62
  ...(parsedUrl.username ? { username: decodeURIComponent(parsedUrl.username) } : {}),
57
63
  ...(parsedUrl.password ? { password: decodeURIComponent(parsedUrl.password) } : {}),
58
64
  {{/unless}}
@@ -1,6 +1,7 @@
1
1
  {{#if hasRateLimiting}}
2
2
  import { Inject, Injectable } from "@nestjs/common";
3
- import { ThrottlerStorage, type ThrottlerStorage as ThrottlerStorageService } from "@nestjs/throttler";
3
+ import { ThrottlerStorage } from "@nestjs/throttler";
4
+ import type { ThrottlerStorage as ThrottlerStorageService } from "@nestjs/throttler";
4
5
  import { TRPCError } from "@trpc/server";
5
6
  import {
6
7
  MiddlewareOptions,
@@ -40,6 +40,9 @@ import { eq } from "drizzle-orm";
40
40
  {{else if (and hasHealthCheck hasDatabase (or usePrisma useTypeorm useDrizzle))}}
41
41
  import { DbService } from "../src/database/db.service.js";
42
42
  {{/if}}
43
+ {{#if (and hasMongodb useTypeorm hasHealthCheck)}}
44
+ import { Todo } from "../src/database/entities/todo.entity.js";
45
+ {{/if}}
43
46
 
44
47
  describe("AppController (e2e)", () => {
45
48
  let app: INestApplication<App>;
@@ -232,8 +235,8 @@ describe("AppController (e2e)", () => {
232
235
  {{#if useSwaggerScalar}}
233
236
  app.use("/docs", apiReference({
234
237
  content: generatedOpenApi,
235
- {{#if (eq httpProvider "fastify")}}withFastify: true,{{/if}}
236
238
  theme: "default",
239
+ {{#if (eq httpProvider "fastify")}}withFastify: true,{{/if}}
237
240
  }));
238
241
  {{/if}}
239
242
  {{/if}}
@@ -418,8 +421,8 @@ describe("AppController (e2e)", () => {
418
421
  {{#if (and hasHealthCheck hasDatabase (or usePrisma useTypeorm useDrizzle))}}
419
422
  it("reports database readiness failure with HTTP 503", async () => {
420
423
  const db = app.get(DbService);
421
- const database = {{#if hasMongodb}}db.raw.mongo{{else if isPrisma8}}db.prisma.orm.public.Todo{{else if usePrisma}}db.prisma{{else if useTypeorm}}db.typeorm{{else}}db.drizzle{{/if}};
422
- const query = vi.spyOn(database, {{#if hasMongodb}}{{#if usePrisma}}"$runCommandRaw"{{else}}"command"{{/if}}{{else if isPrisma8}}"all"{{else if usePrisma}}"$queryRaw"{{else if useTypeorm}}"query"{{else if hasSqlite}}"get"{{else}}"execute"{{/if}})
424
+ const database = {{#if hasMongodb}}{{#if useTypeorm}}db.typeorm.getMongoRepository(Todo){{else}}db.raw.mongo{{/if}}{{else if isPrisma8}}db.prisma.orm.public.Todo{{else if usePrisma}}db.prisma{{else if useTypeorm}}db.typeorm{{else}}db.drizzle{{/if}};
425
+ const query = vi.spyOn(database, {{#if hasMongodb}}{{#if usePrisma}}"$runCommandRaw"{{else if useTypeorm}}"countDocuments"{{else}}"command"{{/if}}{{else if isPrisma8}}"all"{{else if usePrisma}}"$queryRaw"{{else if useTypeorm}}"query"{{else if hasSqlite}}"get"{{else}}"execute"{{/if}})
423
426
  .mockRejectedValue(new Error("scenario database outage"));
424
427
  try {
425
428
  const response = await request(getRequestTarget()).get("/health").expect(503);
@@ -434,7 +437,8 @@ describe("AppController (e2e)", () => {
434
437
  {{#if hasTrpc}}
435
438
  it("executes a tRPC query through HTTP", async () => {
436
439
  const response = await request(getRequestTarget())
437
- .get("/trpc/appRouter.getHello").expect(200);
440
+ .get("/trpc/appRouter.getHello")
441
+ .expect(200);
438
442
  expect(response.body.result.data).toBe("Hello World!");
439
443
  });
440
444
  {{/if}}
@@ -464,10 +468,10 @@ describe("AppController (e2e)", () => {
464
468
  .set("Origin", "http://localhost:3000").expect(302);
465
469
  const cookies = session.headers["set-cookie"] as string[];
466
470
  expect(cookies).toBeTruthy();
467
- const created = await call("create", { title: "OIDC tRPC todo", completed: false }, cookies).expect(200);
471
+ const created = await call("create", { completed: false, title: "OIDC tRPC todo" }, cookies).expect(200);
468
472
  const todo = (created.body.result?.data?.json ?? created.body.result?.data) as { id: string | number };
469
473
  await call("findAll", undefined, cookies).expect(200);
470
- await call("update", { id: todo.id, data: { completed: true } }, cookies).expect(200);
474
+ await call("update", { data: { completed: true }, id: todo.id }, cookies).expect(200);
471
475
  await call("delete", { id: todo.id }, cookies).expect(200);
472
476
  });
473
477
  {{else if (and hasTrpc hasTodoExample hasAuth)}}
@@ -530,19 +534,19 @@ describe("AppController (e2e)", () => {
530
534
  const server = app.getHttpServer();
531
535
  const create = await request(getRequestTarget(server))
532
536
  .post("/trpc/todoRouter.create")
533
- .send({ title: "Public tRPC todo", completed: false })
537
+ .send({ completed: false, title: "Public tRPC todo" })
534
538
  .expect(200);
535
539
  const created = create.body.result?.data?.json ?? create.body.result?.data;
536
540
  expect(created.title).toBe("Public tRPC todo");
537
541
  const id = created.id as string | number;
538
542
 
539
543
  const list = await request(getRequestTarget(server)).get("/trpc/todoRouter.findAll").expect(200);
540
- const todos = list.body.result?.data?.json ?? list.body.result?.data;
541
- expect(todos).toEqual(expect.arrayContaining([expect.objectContaining({ id })]));
544
+ const todoResults = list.body.result?.data?.json ?? list.body.result?.data;
545
+ expect(todoResults).toEqual(expect.arrayContaining([expect.objectContaining({ id })]));
542
546
 
543
547
  const update = await request(getRequestTarget(server))
544
548
  .post("/trpc/todoRouter.update")
545
- .send({ id, data: { completed: true } })
549
+ .send({ data: { completed: true }, id })
546
550
  .expect(200);
547
551
  const updated = update.body.result?.data?.json ?? update.body.result?.data;
548
552
  expect(updated.completed).toBe(true);
@@ -1,7 +1,7 @@
1
- FROM node:22-alpine
1
+ FROM node:22-trixie-slim
2
2
 
3
3
  {{#if hasSqlite}}
4
- RUN apk add --no-cache python3 make g++
4
+ RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ && rm -rf /var/lib/apt/lists/*
5
5
  {{/if}}
6
6
 
7
7
  WORKDIR /app
@@ -5,6 +5,9 @@ import { AllowAnonymous } from "@thallesp/nestjs-better-auth";
5
5
  {{/if}}
6
6
  {{#if hasDatabase}}
7
7
  import { DbService } from "../database/db.service.js";
8
+ {{#if (and hasMongodb useTypeorm)}}
9
+ import { Todo } from "../database/entities/todo.entity.js";
10
+ {{/if}}
8
11
  {{#if useDrizzle}}
9
12
  import { sql } from "drizzle-orm";
10
13
  {{/if}}
@@ -31,6 +34,8 @@ export class HealthController {
31
34
  {{#if hasMongodb}}
32
35
  {{#if usePrisma}}
33
36
  await this.db.raw.mongo.$runCommandRaw({ ping: 1 });
37
+ {{else if useTypeorm}}
38
+ await this.db.typeorm.getMongoRepository(Todo).countDocuments({}, { limit: 1 });
34
39
  {{else}}
35
40
  await this.db.raw.mongo.command({ ping: 1 });
36
41
  {{/if}}
@@ -12,16 +12,16 @@ import { CreateTodoDto } from "./dto/create-todo.dto.js";
12
12
  import { UpdateTodoDto } from "./dto/update-todo.dto.js";
13
13
 
14
14
  const todoSchema = z.object({
15
+ authorId: z.string().nullable().optional(),
16
+ completed: z.boolean(),
17
+ description: z.string().nullable().optional(),
15
18
  id: z.union([z.string(), z.number()]),
16
19
  title: z.string(),
17
- description: z.string().nullable().optional(),
18
- completed: z.boolean(),
19
- authorId: z.string().nullable().optional(),
20
20
  });
21
21
 
22
22
  const updateTodoInput = z.object({
23
- id: z.union([z.string(), z.number()]),
24
23
  data: UpdateTodoDto.schema,
24
+ id: z.union([z.string(), z.number()]),
25
25
  });
26
26
 
27
27
  @Router({ alias: "todoRouter" })