@nest-arch/tui 0.2.8 → 0.4.3

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 (92) hide show
  1. package/README.md +20 -42
  2. package/dist/cli.js +1 -1
  3. package/dist/constants-B5MuV-iH.js +1 -0
  4. package/dist/index.js +365 -98
  5. package/dist/templates/addons/husky/pre-commit.hbs +11 -26
  6. package/dist/templates/base/.env.example.hbs +13 -2
  7. package/dist/templates/base/.env.hbs +11 -2
  8. package/dist/templates/base/.gitignore.hbs +2 -1
  9. package/dist/templates/base/.yarnrc.yml.hbs +0 -5
  10. package/dist/templates/base/README.md.hbs +49 -5
  11. package/dist/templates/base/nest-cli.json.hbs +2 -1
  12. package/dist/templates/base/pnpm-workspace.yaml.hbs +15 -0
  13. package/dist/templates/base/prisma.config.ts.hbs +32 -6
  14. package/dist/templates/base/src/app.controller.spec.ts.hbs +3 -11
  15. package/dist/templates/base/src/app.controller.ts.hbs +2 -2
  16. package/dist/templates/base/src/app.module.ts.hbs +76 -5
  17. package/dist/templates/base/src/app.router.ts.hbs +3 -4
  18. package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +7 -0
  19. package/dist/templates/base/src/database/adapters/mongo/prisma-mongo.adapter.ts.hbs +36 -4
  20. package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +17 -12
  21. package/dist/templates/base/src/database/adapters/sql/drizzle-sql.adapter.ts.hbs +9 -0
  22. package/dist/templates/base/src/database/adapters/sql/prisma-sql.adapter.ts.hbs +8 -5
  23. package/dist/templates/base/src/database/adapters/sql/raw-sql.adapter.ts.hbs +37 -0
  24. package/dist/templates/base/src/database/adapters/sql/typeorm-sql.adapter.ts.hbs +8 -3
  25. package/dist/templates/base/src/database/db.module.ts.hbs +55 -42
  26. package/dist/templates/base/src/database/db.service.ts.hbs +60 -6
  27. package/dist/templates/base/src/database/entities/app.entity.ts.hbs +25 -0
  28. package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +12 -7
  29. package/dist/templates/base/src/database/index.ts.hbs +20 -0
  30. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +80 -7
  31. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +23 -4
  32. package/dist/templates/base/src/libs/middlewares/auth.trpc.middleware.ts.hbs +47 -0
  33. package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +43 -0
  34. package/dist/templates/base/src/libs/trpc/error.handler.ts.hbs +1 -1
  35. package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +6 -3
  36. package/dist/templates/base/src/main.ts.hbs +34 -10
  37. package/dist/templates/base/src/modules/users/entities/user.entity.ts.hbs +22 -0
  38. package/dist/templates/base/test/app.e2e-spec.ts.hbs +841 -6
  39. package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +84 -0
  40. package/dist/templates/base/tsconfig.build.json.hbs +2 -0
  41. package/dist/templates/base/vitest.config.e2e.ts.hbs +4 -3
  42. package/dist/templates/base/vitest.config.ts.hbs +4 -3
  43. package/dist/templates/base/yarn.lock.hbs +1 -0
  44. package/dist/templates/extras/docker/.dockerignore.hbs +8 -1
  45. package/dist/templates/extras/docker/Dockerfile.hbs +11 -10
  46. package/dist/templates/extras/health/src/health/health.controller.ts.hbs +54 -0
  47. package/dist/templates/extras/health/src/health/health.module.ts.hbs +9 -0
  48. package/dist/templates/extras/swagger/create-docs.ts.hbs +13 -22
  49. package/dist/templates/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -10
  50. package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +4 -0
  51. package/dist/templates/feature/graphql/src/graphql/orm/drizzle/drizzle-graphql.module.ts.hbs +6 -4
  52. package/dist/templates/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
  53. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +210 -47
  54. package/dist/templates/feature/rest/todo-example/src/api/todo/dto/create-todo.dto.ts.hbs +12 -0
  55. package/dist/templates/feature/rest/todo-example/src/api/todo/dto/update-todo.dto.ts.hbs +12 -0
  56. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +21 -9
  57. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.module.ts.hbs +1 -3
  58. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +41 -13
  59. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +157 -33
  60. package/dist/templates/monorepo/apps/api-gateway/package.json.hbs +2 -3
  61. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.spec.ts.hbs +6 -1
  62. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.ts.hbs +6 -0
  63. package/dist/templates/monorepo/apps/api-gateway/src/app.module.ts.hbs +32 -1
  64. package/dist/templates/monorepo/apps/api-gateway/src/app.service.ts.hbs +1 -1
  65. package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +13 -4
  66. package/dist/templates/monorepo/apps/api-gateway/test/app.e2e-spec.ts.hbs +45 -1
  67. package/dist/templates/monorepo/apps/api-gateway/tsconfig.build.json.hbs +2 -1
  68. package/dist/templates/monorepo/apps/api-gateway/vitest.config.e2e.ts.hbs +3 -2
  69. package/dist/templates/monorepo/apps/api-gateway/vitest.config.ts.hbs +3 -2
  70. package/dist/templates/monorepo/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -10
  71. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
  72. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +151 -42
  73. package/dist/templates/monorepo/packages/env/src/api-gateway.ts.hbs +16 -3
  74. package/dist/templates/monorepo/packages/shared/package.json.hbs +1 -0
  75. package/dist/templates/monorepo/packages/shared/src/auth.ts.hbs +6 -0
  76. package/dist/templates/monorepo/root/.gitignore.hbs +1 -0
  77. package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +19 -2
  78. package/dist/templates/monorepo/root/tsconfig.json.hbs +1 -1
  79. package/dist/templates/monorepo/root/turbo.json.hbs +2 -0
  80. package/dist/templates/single-project/architecture/nest-api/.env.hbs +9 -1
  81. package/dist/templates/single-project/architecture/nest-api/package.json.hbs +1 -2
  82. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +33 -9
  83. package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +3 -3
  84. package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +18 -0
  85. package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +26 -0
  86. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +23 -4
  87. package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +6 -2
  88. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +16 -5
  89. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +70 -0
  90. package/dist/templates/single-project/shared/config/env.ts.hbs +38 -3
  91. package/package.json +5 -2
  92. package/dist/constants-Cgtoxhmd.js +0 -1
@@ -1,28 +1,39 @@
1
+ {{#if hasDatabase}}
1
2
  {{#if (and hasAuth hasDatabase)}}
2
- import { UseGuards } from "@nestjs/common";
3
+ import { BadRequestException, NotFoundException, UseGuards } from "@nestjs/common";
4
+ {{else}}
5
+ import { BadRequestException, NotFoundException } from "@nestjs/common";
3
6
  {{/if}}
4
- import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
5
7
  {{#if (and hasAuth hasDatabase)}}
6
8
  import { UserEntity } from "../../auth/decorators/index.js";
7
9
  import { GqlAuthGuard } from "../../auth/guards/gql-auth.guard.js";
8
10
  {{/if}}
9
- import { DbService } from "@repo/db";
10
- import { CreateTodoInput } from "../dto/create-todo.input.js";
11
- import { UpdateTodoInput } from "../dto/update-todo.input.js";
11
+ import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
12
12
  {{#if useTypeorm}}
13
- import { GraphRelationBuilder } from "typeorm-relations-graphql";
14
13
  import { GraphQLResolveInfo } from "graphql";
14
+ import { GraphRelationBuilder } from "typeorm-relations-graphql";
15
+ import { Todo as TodoEntity } from "@repo/db/src/entities/todo.entity.js";
15
16
  {{/if}}
16
17
  {{#if useDrizzle}}
18
+ import { and, eq } from "drizzle-orm";
17
19
  import * as schema from "@repo/db/src/orm/drizzle/schema.js";
18
20
  {{/if}}
19
- {{#if (and hasAuth hasDatabase)}}
21
+ import { DbService } from "@repo/db";
22
+ import { CreateTodoInput } from "../dto/create-todo.input.js";
23
+ import { UpdateTodoInput } from "../dto/update-todo.input.js";
24
+ import { Todo } from "../models/todo.model.js";
25
+ {{else}}
26
+ import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
27
+ import { CreateTodoInput } from "../dto/create-todo.input.js";
28
+ import { UpdateTodoInput } from "../dto/update-todo.input.js";
20
29
  import { Todo } from "../models/todo.model.js";
21
30
  {{/if}}
22
31
 
23
32
  @Resolver(() => Todo)
24
33
  export class TodoResolver {
34
+ {{#if hasDatabase}}
25
35
  constructor(private readonly db: DbService) {}
36
+ {{/if}}
26
37
 
27
38
  @Query(() => String)
28
39
  hello(): string {
@@ -30,84 +41,182 @@ export class TodoResolver {
30
41
  }{{#if hasDatabase}}
31
42
 
32
43
  @Query(() => [Todo])
33
- todos({{#if useTypeorm}}info: GraphQLResolveInfo{{/if}}) {
44
+ {{#if hasAuth}}
45
+ @UseGuards(GqlAuthGuard)
46
+ {{/if}}
47
+ todos({{#if useTypeorm}}info: GraphQLResolveInfo, {{/if}}{{#if hasAuth}}@UserEntity() user: { id: string }{{/if}}) {
34
48
  {{#if useTypeorm}}
35
- const builder = new GraphRelationBuilder(this.db.getDataSource());
49
+ const builder = new GraphRelationBuilder(this.db.typeorm);
36
50
  const relations = builder.buildForQuery(Todo, info, "todos");
37
- return this.db.getRepository(Todo).find({ relations });
51
+ {{#if hasAuth}}
52
+ return this.db.typeorm.getRepository(TodoEntity).find({ relations, where: { authorId: user.id } });
53
+ {{else}}
54
+ return this.db.typeorm.getRepository(TodoEntity).find({ relations });
55
+ {{/if}}
56
+ {{else if isPrisma8}}
57
+ {{#if hasAuth}}
58
+ return ((this.db.prisma.orm.public as any).Todo).where({ authorId: user.id }).all();
59
+ {{else}}
60
+ return ((this.db.prisma.orm.public as any).Todo).all();
61
+ {{/if}}
38
62
  {{else if usePrisma}}
63
+ {{#if hasAuth}}
64
+ return this.db.prisma.todo.findMany({ where: { authorId: user.id } });
65
+ {{else}}
39
66
  return this.db.prisma.todo.findMany();
67
+ {{/if}}
40
68
  {{else if useDrizzle}}
41
- return this.db.select().from(schema.todos);
69
+ {{#if hasAuth}}
70
+ return this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.authorId, user.id));
71
+ {{else}}
72
+ return this.db.drizzle.select().from(schema.todos);
73
+ {{/if}}
42
74
  {{else}}
43
75
  return [];
44
76
  {{/if}}
45
77
  }
46
78
 
79
+ {{#if hasAuth}}
47
80
  @UseGuards(GqlAuthGuard)
81
+ {{/if}}
48
82
  @Mutation(() => Todo)
49
- createTodo(
83
+ async createTodo(
84
+ {{#if hasAuth}}
50
85
  @UserEntity() user: { id: string },
86
+ {{/if}}
51
87
  @Args("input") input: CreateTodoInput{{#if useTypeorm}}, info: GraphQLResolveInfo{{/if}}
52
88
  ) {
53
- {{#if useTypeorm}}
54
- const entity = this.db.getRepository(Todo).create(input);
55
- const saved = await this.db.getRepository(Todo).save(entity);
56
- const builder = new GraphRelationBuilder(this.db.getDataSource());
89
+ if (!input.title.trim()) throw new BadRequestException("Title must not be empty");
90
+ {{#if isPrisma8}}
91
+ {{#if hasAuth}}
92
+ return ((this.db.prisma.orm.public as any).Todo).create({ ...input, authorId: user.id });
93
+ {{else}}
94
+ return ((this.db.prisma.orm.public as any).Todo).create(input);
95
+ {{/if}}
96
+ {{else if usePrisma}}
97
+ {{#if hasAuth}}
98
+ const todo = await this.db.prisma.todo.create({ data: { ...input, authorId: user.id } });
99
+ {{else}}
100
+ const todo = await this.db.prisma.todo.create({ data: input });
101
+ {{/if}}
102
+ return todo;
103
+ {{else if useTypeorm}}
104
+ {{#if hasAuth}}
105
+ const entity = this.db.typeorm.getRepository(TodoEntity).create({ ...input, authorId: user.id });
106
+ {{else}}
107
+ const entity = this.db.typeorm.getRepository(TodoEntity).create(input);
108
+ {{/if}}
109
+ const saved = await this.db.typeorm.getRepository(TodoEntity).save(entity);
110
+ const builder = new GraphRelationBuilder(this.db.typeorm);
57
111
  const relations = builder.buildForQuery(Todo, info, "createTodo");
58
- return this.db.getRepository(Todo).findOneOrFail({
59
- where: { id: saved.id },
112
+ return this.db.typeorm.getRepository(TodoEntity).findOneOrFail({
60
113
  relations,
61
- });
62
- {{else if usePrisma}}
63
- return this.db.prisma.todo.create({
64
- data: { ...input, authorId: user.id },
114
+ where: { id: saved.id },
65
115
  });
66
116
  {{else if useDrizzle}}
67
- const [todo] = await this.db.insert(schema.todos).values(input).returning();
117
+ {{#if hasAuth}}
118
+ const [todo] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId: user.id }).returning();
119
+ {{else}}
120
+ const [todo] = await this.db.drizzle.insert(schema.todos).values(input).returning();
121
+ {{/if}}
122
+ if (!todo) throw new NotFoundException("Todo not found");
68
123
  return todo;
69
124
  {{else}}
70
125
  return { id: "1", ...input };
71
126
  {{/if}}
72
127
  }
73
128
 
129
+ {{#if hasAuth}}
74
130
  @UseGuards(GqlAuthGuard)
131
+ {{/if}}
75
132
  @Mutation(() => Todo)
76
- updateTodo(@Args("input") input: UpdateTodoInput{{#if useTypeorm}}, info: GraphQLResolveInfo{{/if}}) {
133
+ async updateTodo(
134
+ {{#if hasAuth}}
135
+ @UserEntity() user: { id: string },
136
+ {{/if}}
137
+ @Args("input") input: UpdateTodoInput{{#if useTypeorm}}, info: GraphQLResolveInfo{{/if}}
138
+ ) {
77
139
  const { id, ...data } = input;
78
- {{#if useTypeorm}}
79
- await this.db.getRepository(Todo).update(id, data);
80
- const builder = new GraphRelationBuilder(this.db.getDataSource());
140
+ if (data.title !== undefined && !data.title.trim()) throw new BadRequestException("Title must not be empty");
141
+ {{#if isPrisma8}}
142
+ {{#if hasAuth}}
143
+ return ((this.db.prisma.orm.public as any).Todo).where({ authorId: user.id, id }).update(data);
144
+ {{else}}
145
+ return ((this.db.prisma.orm.public as any).Todo).where({ id }).update(data);
146
+ {{/if}}
147
+ {{else if usePrisma}}
148
+ {{#if hasAuth}}
149
+ const result = await this.db.prisma.todo.update({ data, where: { authorId: user.id, id } });
150
+ {{else}}
151
+ const result = await this.db.prisma.todo.update({ data, where: { id } });
152
+ {{/if}}
153
+ return result;
154
+ {{else if useTypeorm}}
155
+ {{#if hasAuth}}
156
+ const updateResult = await this.db.typeorm.getRepository(TodoEntity).update({ authorId: user.id, id }, data);
157
+ {{else}}
158
+ const updateResult = await this.db.typeorm.getRepository(TodoEntity).update(id, data);
159
+ {{/if}}
160
+ if ((updateResult.affected ?? 0) === 0) throw new NotFoundException("Todo not found");
161
+ const builder = new GraphRelationBuilder(this.db.typeorm);
81
162
  const relations = builder.buildForQuery(Todo, info, "updateTodo");
82
- return this.db.getRepository(Todo).findOneOrFail({
83
- where: { id },
163
+ return this.db.typeorm.getRepository(TodoEntity).findOneOrFail({
84
164
  relations,
165
+ where: { id },
85
166
  });
86
- {{else if usePrisma}}
87
- return this.db.prisma.todo.update({ where: { id }, data });
88
167
  {{else if useDrizzle}}
89
- const [todo] = await this.db
90
- .update(schema.todos)
91
- .set(data)
92
- .where(eq(schema.todos.id, id))
93
- .returning();
168
+ {{#if hasAuth}}
169
+ const [todo] = await this.db.drizzle.update(schema.todos).set(data).where(and(eq(schema.todos.authorId, user.id), eq(schema.todos.id, Number(id)))).returning();
170
+ {{else}}
171
+ const [todo] = await this.db.drizzle.update(schema.todos).set(data).where(eq(schema.todos.id, Number(id))).returning();
172
+ {{/if}}
173
+ if (!todo) throw new NotFoundException("Todo not found");
94
174
  return todo;
95
175
  {{else}}
96
176
  return { id, ...data };
97
177
  {{/if}}
98
178
  }
99
179
 
180
+ {{#if hasAuth}}
100
181
  @UseGuards(GqlAuthGuard)
182
+ {{/if}}
101
183
  @Mutation(() => Boolean)
102
- async deleteTodo(@Args("id") id: string): Promise<boolean> {
103
- {{#if useTypeorm}}
104
- const result = await this.db.getRepository(Todo).delete(id);
105
- return (result.affected ?? 0) > 0;
184
+ async deleteTodo(
185
+ {{#if hasAuth}}
186
+ @UserEntity() user: { id: string },
187
+ {{/if}}
188
+ @Args("id") id: string
189
+ ): Promise<boolean> {
190
+ {{#if isPrisma8}}
191
+ {{#if hasAuth}}
192
+ await ((this.db.prisma.orm.public as any).Todo).where({ authorId: user.id, id }).delete();
193
+ {{else}}
194
+ await ((this.db.prisma.orm.public as any).Todo).where({ id }).delete();
195
+ {{/if}}
196
+ return true;
106
197
  {{else if usePrisma}}
107
- await this.db.prisma.todo.delete({ where: { id } });
198
+ {{#if hasAuth}}
199
+ const result = await this.db.prisma.todo.deleteMany({ where: { authorId: user.id, id } });
200
+ {{else}}
201
+ const result = await this.db.prisma.todo.deleteMany({ where: { id } });
202
+ {{/if}}
203
+ if (result.count === 0) throw new NotFoundException("Todo not found");
204
+ return true;
205
+ {{else if useTypeorm}}
206
+ {{#if hasAuth}}
207
+ const result = await this.db.typeorm.getRepository(TodoEntity).delete({ authorId: user.id, id });
208
+ {{else}}
209
+ const result = await this.db.typeorm.getRepository(TodoEntity).delete(id);
210
+ {{/if}}
211
+ if ((result.affected ?? 0) === 0) throw new NotFoundException("Todo not found");
108
212
  return true;
109
213
  {{else if useDrizzle}}
110
- await this.db.delete(schema.todos).where(eq(schema.todos.id, id));
214
+ {{#if hasAuth}}
215
+ const deleted = await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, user.id), eq(schema.todos.id, Number(id)))).returning();
216
+ {{else}}
217
+ const deleted = await this.db.drizzle.delete(schema.todos).where(eq(schema.todos.id, Number(id))).returning();
218
+ {{/if}}
219
+ if (deleted.length === 0) throw new NotFoundException("Todo not found");
111
220
  return true;
112
221
  {{else}}
113
222
  return true;
@@ -1,6 +1,6 @@
1
1
  import "dotenv/config";
2
2
  import { createEnv } from "@t3-oss/env-core";
3
- import * as z from "zod";
3
+ import { z } from "zod";
4
4
 
5
5
  export const env = createEnv({
6
6
  /**
@@ -31,8 +31,9 @@ export const env = createEnv({
31
31
  .default("development"),
32
32
  PORT: z
33
33
  .string()
34
- .transform((val) => Number.parseInt(val, 10))
34
+ .transform(Number)
35
35
  .default(3000),
36
+ TRUSTED_PROXY_IPS: z.string().default("{{#each trustedProxyIps}}{{#unless @first}},{{/unless}}{{this}}{{/each}}"),
36
37
  {{#if hasPostgres}}
37
38
  POSTGRES_URL: z.url(),
38
39
  {{/if}}
@@ -51,9 +52,21 @@ export const env = createEnv({
51
52
  .transform((val) => val === "true")
52
53
  .default(true),
53
54
  {{/if}}
54
- {{#if (and (eq auth "passport") hasDatabase)}}
55
+ {{#if (eq auth "passport")}}
55
56
  JWT_ACCESS_SECRET: z.string().min(32),
57
+ {{#if hasDatabase}}
56
58
  JWT_REFRESH_SECRET: z.string().min(32),
57
59
  {{/if}}
60
+ {{/if}}
61
+ {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
62
+ BETTER_AUTH_SECRET: z.string().min(32),
63
+ BETTER_AUTH_URL: z.url(),
64
+ {{#if (eq auth "better-auth:stateless")}}
65
+ OIDC_PROVIDER_ID: z.string().min(1),
66
+ OIDC_CLIENT_ID: z.string().min(1),
67
+ OIDC_CLIENT_SECRET: z.string().min(1),
68
+ OIDC_DISCOVERY_URL: z.url(),
69
+ {{/if}}
70
+ {{/if}}
58
71
  },
59
72
  });
@@ -4,6 +4,7 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
7
+ "./auth": "./src/auth.ts",
7
8
  "./*": "./src/*"
8
9
  },
9
10
  "dependencies": {
@@ -0,0 +1,6 @@
1
+ /** Public authentication profile data safe to share with client packages. */
2
+ export interface AuthUserProfile {
3
+ id: string;
4
+ email: string;
5
+ name?: string;
6
+ }
@@ -13,6 +13,7 @@ prisma/generated
13
13
 
14
14
  # Environment variables
15
15
  .env
16
+ .env.test
16
17
  .env*.local
17
18
 
18
19
  # IDEs and editors
@@ -2,8 +2,16 @@ packages:
2
2
  - apps/*
3
3
  - packages/*
4
4
  allowBuilds:
5
+ {{#if useDrizzle}}
6
+ esbuild: true
7
+ {{/if}}
8
+ protobufjs: true
5
9
  '@nestjs/core': true
6
10
  esbuild: true
11
+ {{#if usePrisma}}
12
+ prisma: true
13
+ '@prisma/engines': true
14
+ {{/if}}
7
15
  {{#if swagger}}
8
16
  '@scarf/scarf': true
9
17
  {{/if}}
@@ -16,8 +24,17 @@ allowBuilds:
16
24
  {{#if hasSqlite}}
17
25
  better-sqlite3: true
18
26
  {{/if}}
27
+ {{#if isPrisma8}}
28
+ esbuild: true
29
+ msgpackr-extract: true
30
+ workerd: true
31
+ {{/if}}
19
32
  catalog:
20
- "@types/node": ^22.13.14
33
+ "@nestjs/common": ^12.0.1
34
+ "@nestjs/core": ^12.0.1
35
+ "@types/node": ^24.0.0
21
36
  dotenv: ^17.2.2
22
- typescript: ^6
37
+ reflect-metadata: ^0.2.2
38
+ rxjs: ^7.8.1
39
+ typescript: {{#if useTypescript7}}^7.0.0{{else}}^6.0.2{{/if}}
23
40
  zod: ^4.4.3
@@ -1,3 +1,3 @@
1
1
  {
2
- "extends": "@repo-config/config/tsconfig.base.json"
2
+ "extends": "@repo/config/tsconfig.base.json"
3
3
  }
@@ -10,6 +10,8 @@
10
10
  "lint": {
11
11
  "dependsOn": ["^lint"]
12
12
  },
13
+ "test": { "cache": false },
14
+ "test:e2e": { "cache": false },
13
15
  "check-types": {
14
16
  "dependsOn": ["^check-types"]
15
17
  },
@@ -16,11 +16,19 @@ SQLITE_PATH=file:./src/database/dev.db
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
28
+ {{#if (eq auth "better-auth:stateless")}}
29
+ OIDC_PROVIDER_ID=company-oidc
30
+ OIDC_CLIENT_ID=replace-with-provider-client-id
31
+ OIDC_CLIENT_SECRET=replace-with-provider-client-secret
32
+ OIDC_DISCOVERY_URL=https://identity.example.com/.well-known/openid-configuration
33
+ {{/if}}
26
34
  {{/if}}
@@ -20,7 +20,7 @@
20
20
  "test:debug": "vitest --inspect-brk --no-file-parallelism",
21
21
  "test:e2e": "vitest run --config ./vitest.config.e2e.ts"{{#if hasUltracite}},
22
22
  "check": "ultracite check",
23
- "fix": "ultracite fix"{{/if}}{{#if hasHusky}},
23
+ "fix": "ultracite fix --ignore-pattern migrations/**"{{/if}}{{#if hasHusky}},
24
24
  "prepare": "husky"{{/if}}
25
25
  },
26
26
  "dependencies": {
@@ -46,7 +46,6 @@
46
46
  "source-map-support": "^0.5.21",
47
47
  "supertest": "^7.0.0",
48
48
  "typescript": "^6.0.2",
49
- "vite-tsconfig-paths": "^5.1.4",
50
49
  "vitest": "^4.1.2"
51
50
  }
52
51
  }
@@ -2,10 +2,18 @@ import { NestFactory } from "@nestjs/core";
2
2
  {{#if (eq httpProvider "fastify")}}
3
3
  import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify";
4
4
  {{/if}}
5
+ {{#if (includes addons "nestjs-zod")}}
6
+ import { Logger } from "@nestjs/common";
7
+ import { ZodValidationPipe } from "nestjs-zod";
8
+ {{else}}
5
9
  import { Logger, StandardSchemaValidationPipe } from "@nestjs/common";
10
+ {{/if}}
6
11
 
7
12
  import { AppModule } from "./app.module.js";
8
13
  import { env } from "./config/env.js";
14
+ {{#if isPrisma8}}
15
+ import { connectDatabase } from "./prisma/db.js";
16
+ {{/if}}
9
17
  {{#if swagger}}
10
18
  import { createDocs } from "./docs/create-docs.js";
11
19
  {{/if}}
@@ -16,18 +24,34 @@ import { apiReference } from "@scalar/nestjs-api-reference";
16
24
  import { SwaggerModule } from "@nestjs/swagger";
17
25
  {{/if}}
18
26
  {{#if (and swagger (or (eq auth "better-auth") (eq auth "better-auth:stateless")))}}
19
- import { auth } from "./auth/auth.js";
27
+ import { AuthService } from "@thallesp/nestjs-better-auth";
20
28
  {{/if}}
21
29
 
22
30
  async function bootstrap() {
23
- const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter()){{else}}(AppModule){{/if}};
31
+ const trustedProxyIps = env.TRUSTED_PROXY_IPS
32
+ .split(",")
33
+ .map((ip) => ip.trim())
34
+ .filter(Boolean);
35
+ {{#if isPrisma8}}
36
+ await connectDatabase();
37
+ {{/if}}
38
+ const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter({ trustProxy: trustedProxyIps })){{else}}(AppModule){{/if}};
39
+ {{#unless (eq httpProvider "fastify")}}
40
+ if (trustedProxyIps.length > 0) {
41
+ app.getHttpAdapter().getInstance().set("trust proxy", trustedProxyIps);
42
+ }
43
+ {{/unless}}
24
44
  app.enableCors({
25
45
  origin: env.CORS_ORIGIN,
26
46
  });
27
47
  {{#if hasRest}}
28
48
  app.setGlobalPrefix("api");
29
49
  {{/if}}
50
+ {{#if (includes addons "nestjs-zod")}}
51
+ app.useGlobalPipes(new ZodValidationPipe());
52
+ {{else}}
30
53
  app.useGlobalPipes(new StandardSchemaValidationPipe());
54
+ {{/if}}
31
55
  app.enableShutdownHooks();
32
56
  {{#if swagger}}
33
57
  if (env.NODE_ENV === "development") {
@@ -43,7 +67,7 @@ async function bootstrap() {
43
67
  integrations: {
44
68
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
45
69
  betterAuth: {
46
- auth,
70
+ auth: app.get(AuthService).instance,
47
71
  pathPrefix: "/api/auth",
48
72
  },
49
73
  {{/if}}
@@ -70,7 +94,7 @@ async function bootstrap() {
70
94
  integrations: {
71
95
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
72
96
  betterAuth: {
73
- auth,
97
+ auth: app.get(AuthService).instance,
74
98
  pathPrefix: "/api/auth",
75
99
  },
76
100
  {{/if}}
@@ -87,17 +111,17 @@ async function bootstrap() {
87
111
  });
88
112
  SwaggerModule.setup(docsPath, app, document);
89
113
  {{/if}}
90
- Logger.log(`Swagger documentation available at http://localhost:${env.PORT ?? 3000}${docsPath}`, "SwaggerDocs");
114
+ Logger.log(`Swagger documentation available at http://localhost:${env.PORT}${docsPath}`, "SwaggerDocs");
91
115
  }
92
116
  {{/if}}
93
117
 
94
- await app.listen(env.PORT ?? 3000);
118
+ await app.listen(env.PORT);
95
119
  {{#if hasRest}}
96
- Logger.log(`Started backend server on http://localhost:${env.PORT ?? 3000}/api`, "Main");
120
+ Logger.log(`Started backend server on http://localhost:${env.PORT}/api`, "Main");
97
121
  {{else if hasTrpc}}
98
- Logger.log(`Started trpc server on http://localhost:${env.PORT ?? 3000}/trpc`, "Main");
122
+ Logger.log(`Started trpc server on http://localhost:${env.PORT}/trpc`, "Main");
99
123
  {{else}}
100
- Logger.log(`Started server on http://localhost:${env.PORT ?? 3000}`, "Main");
124
+ Logger.log(`Started server on http://localhost:${env.PORT}`, "Main");
101
125
  {{/if}}
102
126
  }
103
127
  await bootstrap();
@@ -16,7 +16,7 @@ import { apiReference } from "@scalar/nestjs-api-reference";
16
16
  import { SwaggerModule } from "@nestjs/swagger";
17
17
  {{/if}}
18
18
  {{#if (and swagger (or (eq auth "better-auth") (eq auth "better-auth:stateless")))}}
19
- import { auth } from "./auth/auth.js";
19
+ import { AuthService } from "@thallesp/nestjs-better-auth";
20
20
  {{/if}}
21
21
 
22
22
  async function bootstrap() {
@@ -39,7 +39,7 @@ async function bootstrap() {
39
39
  integrations: {
40
40
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
41
41
  betterAuth: {
42
- auth,
42
+ auth: app.get(AuthService).instance,
43
43
  pathPrefix: "/api/auth",
44
44
  },
45
45
  {{/if}}
@@ -66,7 +66,7 @@ async function bootstrap() {
66
66
  integrations: {
67
67
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
68
68
  betterAuth: {
69
- auth,
69
+ auth: app.get(AuthService).instance,
70
70
  pathPrefix: "/api/auth",
71
71
  },
72
72
  {{/if}}
@@ -1,6 +1,24 @@
1
1
  PORT=3000
2
2
  MICROSERVICE_PORT=3001
3
3
  CORS_ORIGIN=http://localhost:3001
4
+ {{#if (includes microservices "redis")}}
5
+ REDIS_URL=redis://127.0.0.1:6379
6
+ {{/if}}
7
+ {{#if (includes microservices "mqtt")}}
8
+ MQTT_URL=mqtt://127.0.0.1:1883
9
+ {{/if}}
10
+ {{#if (includes microservices "nats")}}
11
+ NATS_URL=nats://127.0.0.1:4222
12
+ {{/if}}
13
+ {{#if (includes microservices "rabbitmq")}}
14
+ RABBITMQ_URL=amqp://127.0.0.1:5672
15
+ {{/if}}
16
+ {{#if (includes microservices "kafka")}}
17
+ KAFKA_BROKER=127.0.0.1:9092
18
+ {{/if}}
19
+ {{#if (includes microservices "grpc")}}
20
+ GRPC_URL=0.0.0.0:5000
21
+ {{/if}}
4
22
  {{#if hasGraphql}}
5
23
  GRAPHQL_PLAYGROUND=true
6
24
  {{/if}}
@@ -0,0 +1,26 @@
1
+ syntax = "proto3";
2
+
3
+ package app;
4
+
5
+ service AppService {
6
+ rpc Ping (PingRequest) returns (PingResponse);
7
+ rpc Log (LogRequest) returns (LogResponse);
8
+ }
9
+
10
+ message PingRequest {}
11
+
12
+ message PingResponse {
13
+ string message = 1;
14
+ }
15
+
16
+ message LogRequest {
17
+ string id = 1;
18
+ string source = 2;
19
+ string payload = 3;
20
+ }
21
+
22
+ message LogResponse {
23
+ string id = 1;
24
+ string source = 2;
25
+ string payload = 3;
26
+ }
@@ -1,4 +1,5 @@
1
- import { Controller, EventPattern, MessagePattern } from "@nestjs/common";
1
+ import { Controller, Get } from "@nestjs/common";
2
+ import { {{#if (includes microservices "grpc")}}GrpcMethod{{else}}MessagePattern{{/if}} } from "@nestjs/microservices";
2
3
 
3
4
  import { AppService } from "./app.service.js";
4
5
 
@@ -6,13 +7,31 @@ import { AppService } from "./app.service.js";
6
7
  export class AppController {
7
8
  constructor(private readonly appService: AppService) {}
8
9
 
10
+ @Get()
11
+ getHello(): string {
12
+ return this.appService.getHello();
13
+ }
14
+
15
+ {{#if (includes microservices "grpc")}}
16
+ @GrpcMethod("AppService", "Ping")
17
+ handleGrpcPing(): { message: string } {
18
+ return { message: this.appService.handlePing() };
19
+ }
20
+ @GrpcMethod("AppService", "Log")
21
+ handleGrpcLog(data: { id: string; source: string; payload: string }): { id: string; source: string; payload: string } {
22
+ return data;
23
+ }
24
+ {{else}}
9
25
  @MessagePattern("ping")
10
26
  handlePing(): string {
11
27
  return this.appService.handlePing();
12
28
  }
29
+ {{/if}}
13
30
 
14
- @EventPattern("log")
15
- handleLog(data: Record<string, unknown>): void {
16
- this.appService.handleLog(data);
31
+ {{#unless (includes microservices "grpc")}}
32
+ @MessagePattern("log")
33
+ handleLog(data: Record<string, unknown>): { received: Record<string, unknown> } {
34
+ return this.appService.handleLog(data);
17
35
  }
36
+ {{/unless}}
18
37
  }
@@ -9,11 +9,15 @@ export class AppService {
9
9
  constructor(private readonly db: DbService) {}
10
10
 
11
11
  {{/if}}
12
+ getHello(): string {
13
+ return "Hello World!";
14
+ }
15
+
12
16
  handlePing(): string {
13
17
  return "pong";
14
18
  }
15
19
 
16
- handleLog(data: Record<string, unknown>): void {
17
- console.log("Microservice received:", data);
20
+ handleLog(data: Record<string, unknown>): { received: Record<string, unknown> } {
21
+ return { received: data };
18
22
  }
19
23
  }