@nest-arch/tui 0.4.0 → 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 (64) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/{constants-C3xmDWeK.js → constants-B5MuV-iH.js} +1 -1
  3. package/dist/index.js +188 -134
  4. package/dist/templates/addons/husky/pre-commit.hbs +11 -26
  5. package/dist/templates/base/.env.example.hbs +9 -0
  6. package/dist/templates/base/.env.hbs +7 -0
  7. package/dist/templates/base/README.md.hbs +16 -0
  8. package/dist/templates/base/pnpm-workspace.yaml.hbs +6 -0
  9. package/dist/templates/base/src/app.module.ts.hbs +68 -1
  10. package/dist/templates/base/src/app.router.ts.hbs +1 -2
  11. package/dist/templates/base/src/database/adapters/mongo/prisma-mongo.adapter.ts.hbs +11 -3
  12. package/dist/templates/base/src/database/adapters/sql/prisma-sql.adapter.ts.hbs +1 -1
  13. package/dist/templates/base/src/database/db.module.ts.hbs +24 -8
  14. package/dist/templates/base/src/database/db.service.ts.hbs +20 -0
  15. package/dist/templates/base/src/database/entities/app.entity.ts.hbs +25 -0
  16. package/dist/templates/base/src/database/index.ts.hbs +19 -0
  17. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +42 -11
  18. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -1
  19. package/dist/templates/base/src/libs/middlewares/auth.trpc.middleware.ts.hbs +47 -0
  20. package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +43 -0
  21. package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +6 -3
  22. package/dist/templates/base/src/main.ts.hbs +13 -4
  23. package/dist/templates/base/src/modules/users/entities/user.entity.ts.hbs +22 -0
  24. package/dist/templates/base/test/app.e2e-spec.ts.hbs +670 -37
  25. package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +11 -1
  26. package/dist/templates/extras/docker/.dockerignore.hbs +8 -1
  27. package/dist/templates/extras/docker/Dockerfile.hbs +8 -10
  28. package/dist/templates/extras/health/src/health/health.controller.ts.hbs +54 -0
  29. package/dist/templates/extras/health/src/health/health.module.ts.hbs +9 -0
  30. package/dist/templates/extras/swagger/create-docs.ts.hbs +9 -3
  31. package/dist/templates/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -17
  32. package/dist/templates/feature/graphql/src/graphql/orm/drizzle/drizzle-graphql.module.ts.hbs +3 -2
  33. package/dist/templates/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
  34. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +165 -53
  35. package/dist/templates/feature/rest/todo-example/src/api/todo/dto/create-todo.dto.ts.hbs +12 -0
  36. package/dist/templates/feature/rest/todo-example/src/api/todo/dto/update-todo.dto.ts.hbs +12 -0
  37. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +18 -8
  38. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +41 -13
  39. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +68 -10
  40. package/dist/templates/monorepo/apps/api-gateway/package.json.hbs +2 -2
  41. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.spec.ts.hbs +5 -0
  42. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.ts.hbs +6 -0
  43. package/dist/templates/monorepo/apps/api-gateway/src/app.module.ts.hbs +32 -1
  44. package/dist/templates/monorepo/apps/api-gateway/src/app.service.ts.hbs +1 -1
  45. package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +13 -4
  46. package/dist/templates/monorepo/apps/api-gateway/test/app.e2e-spec.ts.hbs +26 -1
  47. package/dist/templates/monorepo/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -10
  48. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
  49. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +147 -47
  50. package/dist/templates/monorepo/packages/env/src/api-gateway.ts.hbs +7 -0
  51. package/dist/templates/monorepo/packages/shared/package.json.hbs +1 -0
  52. package/dist/templates/monorepo/packages/shared/src/auth.ts.hbs +6 -0
  53. package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +10 -2
  54. package/dist/templates/single-project/architecture/nest-api/.env.hbs +6 -0
  55. package/dist/templates/single-project/architecture/nest-api/package.json.hbs +1 -1
  56. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +13 -4
  57. package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +3 -3
  58. package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
  59. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +10 -4
  60. package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +2 -2
  61. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +3 -3
  62. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +7 -1
  63. package/dist/templates/single-project/shared/config/env.ts.hbs +12 -1
  64. package/package.json +2 -2
@@ -1,8 +1,12 @@
1
1
  {{#if hasTrpc}}
2
- import { Router, Query, Mutation, Ctx, Input } from "nestjs-trpc";
2
+ import { Router, Query, Mutation, Ctx, Input{{#if hasTrpcAndAuth}}, UseMiddlewares{{/if}} } from "nestjs-trpc";
3
+ import { NotFoundException } from "@nestjs/common";
4
+ import { TRPCError } from "@trpc/server";
3
5
  import z from "zod";
4
- {{#if hasTrpcAndAuth}}
6
+ {{#if (and hasTrpcAndAuth isBetterAuth)}}
5
7
  import type { UserSession } from "../../auth/auth.js";
8
+ {{/if}}
9
+ {{#if hasTrpcAndAuth}}
6
10
  import { AuthTRPCMiddleware } from "../../libs/middlewares/auth.trpc.middleware.js";
7
11
  import type { InnerContext } from "../../libs/trpc/inner.context.js";
8
12
  {{/if}}
@@ -11,7 +15,7 @@ import { CreateTodoDto } from "./dto/create-todo.dto.js";
11
15
  import { UpdateTodoDto } from "./dto/update-todo.dto.js";
12
16
 
13
17
  const todoSchema = z.object({
14
- id: z.string(),
18
+ id: z.union([z.string(), z.number()]),
15
19
  title: z.string(),
16
20
  description: z.string().nullable().optional(),
17
21
  completed: z.boolean(),
@@ -19,7 +23,7 @@ const todoSchema = z.object({
19
23
  });
20
24
 
21
25
  const updateTodoInput = z.object({
22
- id: z.string(),
26
+ id: z.union([z.string(), z.number()]),
23
27
  data: UpdateTodoDto.schema,
24
28
  });
25
29
 
@@ -42,14 +46,26 @@ export class TodoRouter {
42
46
 
43
47
  @Mutation({ input: updateTodoInput, output: todoSchema })
44
48
  @UseMiddlewares(AuthTRPCMiddleware)
45
- update(@Ctx() ctx: InnerContext, @Input() input: { id: string; data: UpdateTodoDto }) {
46
- return this.todoService.update(input.id, input.data, ctx.session!.user.id);
49
+ update(@Ctx() ctx: InnerContext, @Input() input: { id: string | number; data: UpdateTodoDto }) {
50
+ return this.todoService.update(String(input.id), input.data, ctx.session!.user.id)
51
+ .catch((error: unknown) => {
52
+ if (error instanceof NotFoundException) {
53
+ throw new TRPCError({ code: "NOT_FOUND", message: error.message });
54
+ }
55
+ throw error;
56
+ });
47
57
  }
48
58
 
49
- @Mutation({ input: z.object({ id: z.string() }) })
59
+ @Mutation({ input: z.object({ id: z.union([z.string(), z.number()]) }) })
50
60
  @UseMiddlewares(AuthTRPCMiddleware)
51
- delete(@Ctx() ctx: InnerContext, @Input() input: { id: string }) {
52
- return this.todoService.delete(input.id, ctx.session!.user.id);
61
+ delete(@Ctx() ctx: InnerContext, @Input() input: { id: string | number }) {
62
+ return this.todoService.delete(String(input.id), ctx.session!.user.id)
63
+ .catch((error: unknown) => {
64
+ if (error instanceof NotFoundException) {
65
+ throw new TRPCError({ code: "NOT_FOUND", message: error.message });
66
+ }
67
+ throw error;
68
+ });
53
69
  }
54
70
  {{else}}
55
71
  @Query({ output: z.array(todoSchema) })
@@ -64,12 +80,24 @@ export class TodoRouter {
64
80
 
65
81
  @Mutation({ input: updateTodoInput, output: todoSchema })
66
82
  update(@Input() input: { id: string; data: UpdateTodoDto }) {
67
- return this.todoService.update(input.id, input.data);
83
+ return this.todoService.update(input.id, input.data)
84
+ .catch((error: unknown) => {
85
+ if (error instanceof NotFoundException) {
86
+ throw new TRPCError({ code: "NOT_FOUND", message: error.message });
87
+ }
88
+ throw error;
89
+ });
68
90
  }
69
91
 
70
- @Mutation({ input: z.object({ id: z.string() }) })
71
- delete(@Input() input: { id: string }) {
72
- return this.todoService.delete(input.id);
92
+ @Mutation({ input: z.object({ id: z.union([z.string(), z.number()]) }) })
93
+ delete(@Input() input: { id: string | number }) {
94
+ return this.todoService.delete(String(input.id))
95
+ .catch((error: unknown) => {
96
+ if (error instanceof NotFoundException) {
97
+ throw new TRPCError({ code: "NOT_FOUND", message: error.message });
98
+ }
99
+ throw error;
100
+ });
73
101
  }
74
102
  {{/if}}
75
103
  }
@@ -32,7 +32,11 @@ export class TodoService {
32
32
  }
33
33
  }
34
34
  {{else}}
35
+ {{#if (and hasDatabase hasAuth)}}
36
+ import { Injectable, NotFoundException } from "@nestjs/common";
37
+ {{else}}
35
38
  import { Injectable } from "@nestjs/common";
39
+ {{/if}}
36
40
  {{#if hasDatabase}}
37
41
  {{#if isMonorepo}}
38
42
  import { DbService } from "@repo/db";
@@ -92,33 +96,87 @@ export class TodoService {
92
96
  const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create({ ...input, authorId }));
93
97
  return todo;
94
98
  {{else if useDrizzle}}
95
- return this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).returning();
99
+ {{#if hasMysql}}
100
+ const [inserted] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).$returningId();
101
+ if (!inserted) {
102
+ throw new NotFoundException("Todo not found");
103
+ }
104
+ const [todo] = await this.db.drizzle.select().from(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, inserted.id)));
105
+ return todo;
106
+ {{else}}
107
+ const [todo] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).returning();
108
+ return todo;
109
+ {{/if}}
96
110
  {{/if}}
97
111
  }
98
112
 
99
113
  async update(id: string, input: UpdateTodoDto, authorId: string) {
100
114
  {{#if isPrisma8}}
101
- return ((this.db.prisma.orm.public as any).Todo).where({ authorId, id }).update(input);
102
- {{else if usePrisma}}
103
- const result = await this.db.prisma.todo.update({ data: input, where: { authorId, id } });
115
+ const result = await ((this.db.prisma.orm.public as any).Todo).where({ authorId, id }).update(input);
116
+ if (!result) {
117
+ throw new NotFoundException("Todo not found");
118
+ }
104
119
  return result;
120
+ {{else if usePrisma}}
121
+ const todo = await this.db.prisma.todo.findFirst({ where: { authorId, id } });
122
+ if (!todo) {
123
+ throw new NotFoundException("Todo not found");
124
+ }
125
+ return this.db.prisma.todo.update({ data: input, where: { id } });
105
126
  {{else if useTypeorm}}
106
- const result = await this.db.typeorm.getRepository(Todo).update({ authorId, id }, input);
107
- return result;
127
+ const repository = this.db.typeorm.getRepository(Todo);
128
+ const result = await repository.update({ authorId, id }, input);
129
+ if (result.affected === 0) {
130
+ throw new NotFoundException("Todo not found");
131
+ }
132
+ const todo = await repository.findOneBy({ authorId, id });
133
+ if (!todo) {
134
+ throw new NotFoundException("Todo not found");
135
+ }
136
+ return todo;
108
137
  {{else if useDrizzle}}
109
- return this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
138
+ {{#if hasMysql}}
139
+ await this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
140
+ const [todo] = await this.db.drizzle.select().from(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
141
+ {{else}}
142
+ const [todo] = await this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
143
+ {{/if}}
144
+ if (!todo) {
145
+ throw new NotFoundException("Todo not found");
146
+ }
147
+ return todo;
110
148
  {{/if}}
111
149
  }
112
150
 
113
151
  async delete(id: string, authorId: string) {
114
152
  {{#if isPrisma8}}
115
- await ((this.db.prisma.orm.public as any).Todo).where({ authorId, id }).delete();
153
+ const count = await ((this.db.prisma.orm.public as any).Todo).where({ authorId, id }).delete();
154
+ if (count === 0) {
155
+ throw new NotFoundException("Todo not found");
156
+ }
116
157
  {{else if usePrisma}}
117
- await this.db.prisma.todo.delete({ where: { authorId, id } });
158
+ const result = await this.db.prisma.todo.deleteMany({ where: { authorId, id } });
159
+ if (result.count === 0) {
160
+ throw new NotFoundException("Todo not found");
161
+ }
118
162
  {{else if useTypeorm}}
119
- await this.db.typeorm.getRepository(Todo).delete({ authorId, id });
163
+ const result = await this.db.typeorm.getRepository(Todo).delete({ authorId, id });
164
+ if (result.affected === 0) {
165
+ throw new NotFoundException("Todo not found");
166
+ }
120
167
  {{else if useDrizzle}}
168
+ {{#if hasMysql}}
169
+ const [existing] = await this.db.drizzle.select().from(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
170
+ if (!existing) {
171
+ throw new NotFoundException("Todo not found");
172
+ }
121
173
  await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
174
+ {{else}}
175
+ const [todo] = await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
176
+ if (!todo) {
177
+ throw new NotFoundException("Todo not found");
178
+ }
179
+ {{/if}}
122
180
  {{/if}}
123
181
  return { message: "Todo deleted successfully" };
124
182
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "{{projectName}}",
2
+ "name": "@repo/api-gateway",
3
3
  "version": "0.0.1",
4
4
  "private": true,
5
5
  "description": "{{description}}",
@@ -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": {
@@ -6,9 +6,14 @@ import { beforeEach, describe, expect, it{{#if (or hasDatabase hasOrm)}}, vi{{/i
6
6
  import { AppController } from "./app.controller.js";
7
7
  import { AppService } from "./app.service.js";
8
8
  {{#if (or hasDatabase hasOrm)}}
9
+ {{#if isMonorepo}}
10
+ import type { ISqlAdapter } from "@repo/db";
11
+ import { DbService } from "@repo/db";
12
+ {{else}}
9
13
  import type { ISqlAdapter } from "./database/adapters/interfaces/db-adapter.interface.js";
10
14
  import { DbService } from "./database/db.service.js";
11
15
  {{/if}}
16
+ {{/if}}
12
17
 
13
18
  {{#if (or hasDatabase hasOrm)}}
14
19
  const mockSqlAdapter: ISqlAdapter = {
@@ -1,4 +1,7 @@
1
1
  import { Controller, Get } from "@nestjs/common";
2
+ {{#if (eq auth "better-auth")}}
3
+ import { AllowAnonymous } from "@thallesp/nestjs-better-auth";
4
+ {{/if}}
2
5
  {{#if swagger}}
3
6
  import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
4
7
  {{/if}}
@@ -16,6 +19,9 @@ export class AppController {
16
19
  @ApiOperation({ summary: "/api" })
17
20
  @ApiResponse({ status: 200, description: "Returns a hello world message" })
18
21
  {{/if}}
22
+ {{#if (eq auth "better-auth")}}
23
+ @AllowAnonymous()
24
+ {{/if}}
19
25
  @Get()
20
26
  getHello(): string {
21
27
  return this.appService.getHello();
@@ -1,7 +1,11 @@
1
1
  import { Module } from "@nestjs/common";
2
2
  {{#if (or hasDatabase hasOrm)}}
3
+ {{#if isMonorepo}}
4
+ import { DbModule } from "@repo/db";
5
+ {{else}}
3
6
  import { DbModule } from "./database/db.module.js";
4
- import { env } from "./config/env.js";
7
+ {{/if}}
8
+ import { env } from "@repo/env/api-gateway";
5
9
  {{/if}}
6
10
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
7
11
  import { AuthModule } from "./auth/auth.module.js";
@@ -17,6 +21,13 @@ import { AppErrorHandler } from "./libs/trpc/error.handler.js";
17
21
  {{#if hasTrpcAndAuth}}
18
22
  import { AuthTRPCMiddleware } from "./libs/middlewares/auth.trpc.middleware.js";
19
23
  {{/if}}
24
+ {{#if (and hasTrpc hasRateLimiting)}}
25
+ import { RateLimitTRPCMiddleware } from "./libs/middlewares/rate-limit.trpc.middleware.js";
26
+ {{/if}}
27
+ {{/if}}
28
+ {{#if hasRateLimiting}}
29
+ import { APP_GUARD } from "@nestjs/core";
30
+ import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
20
31
  {{/if}}
21
32
 
22
33
  import { AppController } from "./app.controller.js";
@@ -51,8 +62,19 @@ import { AppService } from "./app.service.js";
51
62
  TRPCModule.forRoot({
52
63
  context: AppContext,
53
64
  onError: AppErrorHandler,
65
+ {{#if hasRateLimiting}}
66
+ globalMiddlewares: [RateLimitTRPCMiddleware],
67
+ {{/if}}
54
68
  }),
55
69
  {{/if}}
70
+ {{#if hasRateLimiting}}
71
+ ThrottlerModule.forRoot([
72
+ {
73
+ ttl: 60_000,
74
+ limit: 60,
75
+ },
76
+ ]),
77
+ {{/if}}
56
78
  {{#if (and hasTodoExample (or hasRest hasTrpc))}}
57
79
  TodoModule,
58
80
  {{/if}}
@@ -63,6 +85,9 @@ import { AppService } from "./app.service.js";
63
85
  {{#if hasTrpcAndAuth}}
64
86
  AuthTRPCMiddleware,
65
87
  {{/if}}
88
+ {{#if hasRateLimiting}}
89
+ RateLimitTRPCMiddleware,
90
+ {{/if}}
66
91
  AppContext,
67
92
  AppErrorHandler,
68
93
  AppRouter,
@@ -70,6 +95,12 @@ import { AppService } from "./app.service.js";
70
95
  TodoRouter,
71
96
  {{/if}}
72
97
  {{/if}}
98
+ {{#if hasRateLimiting}}
99
+ {
100
+ provide: APP_GUARD,
101
+ useClass: ThrottlerGuard,
102
+ },
103
+ {{/if}}
73
104
  ],
74
105
  })
75
106
  export class AppModule {}
@@ -1,6 +1,6 @@
1
1
  import { Injectable } from "@nestjs/common";
2
2
  {{#if (or hasDatabase hasOrm)}}
3
- import { DbService } from "./database/db.service.js";
3
+ import { DbService } from "@repo/db";
4
4
  {{/if}}
5
5
 
6
6
  @Injectable()
@@ -16,11 +16,20 @@ 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() {
23
- const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter()){{else}}(AppModule){{/if}};
23
+ const trustedProxyIps = env.TRUSTED_PROXY_IPS
24
+ .split(",")
25
+ .map((ip) => ip.trim())
26
+ .filter(Boolean);
27
+ const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter({ trustProxy: trustedProxyIps })){{else}}(AppModule){{/if}};
28
+ {{#unless (eq httpProvider "fastify")}}
29
+ if (trustedProxyIps.length > 0) {
30
+ app.getHttpAdapter().getInstance().set("trust proxy", trustedProxyIps);
31
+ }
32
+ {{/unless}}
24
33
  app.enableCors({
25
34
  origin: env.CORS_ORIGIN,
26
35
  });
@@ -38,7 +47,7 @@ async function bootstrap() {
38
47
  integrations: {
39
48
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
40
49
  betterAuth: {
41
- auth,
50
+ auth: app.get(AuthService).instance,
42
51
  pathPrefix: "/api/auth",
43
52
  },
44
53
  {{/if}}
@@ -65,7 +74,7 @@ async function bootstrap() {
65
74
  integrations: {
66
75
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
67
76
  betterAuth: {
68
- auth,
77
+ auth: app.get(AuthService).instance,
69
78
  pathPrefix: "/api/auth",
70
79
  },
71
80
  {{/if}}
@@ -4,7 +4,7 @@ import { Test, TestingModule } from "@nestjs/testing";
4
4
  import { FastifyAdapter } from "@nestjs/platform-fastify";
5
5
  {{/if}}
6
6
  import request from "supertest";
7
- import { afterEach, beforeEach, describe, it } from "vitest";
7
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
8
  import type { App } from "supertest/types";
9
9
 
10
10
  import { AppModule } from "./../src/app.module.js";
@@ -40,6 +40,31 @@ describe("AppController (e2e)", () => {
40
40
  await request(app.getHttpServer()).post("/").send({ unexpected: true }).expect(404);
41
41
  });
42
42
 
43
+ {{#if (eq auth "better-auth")}}
44
+ it("creates a user and resolves the authenticated session", async () => {
45
+ const email = `monorepo-${Date.now()}@example.com`;
46
+ const signup = await request(app.getHttpServer())
47
+ .post("/api/auth/sign-up/email")
48
+ .set("origin", "http://localhost:3000")
49
+ .send({ email, name: "Monorepo Scenario", password: "Scenario-password-123!" })
50
+ .expect(200);
51
+ const cookies = (signup.headers["set-cookie"] ?? []).map((cookie) => cookie.split(";")[0]);
52
+ expect(cookies.length).toBeGreaterThan(0);
53
+
54
+ const session = await request(app.getHttpServer())
55
+ .get("/api/auth/get-session")
56
+ .set("cookie", cookies.join("; "))
57
+ .expect(200);
58
+ expect(session.body.user.email).toBe(email);
59
+
60
+ const profile = await request(app.getHttpServer())
61
+ .get("/auth-profile")
62
+ .set("cookie", cookies.join("; "))
63
+ .expect(200);
64
+ expect(profile.body).toEqual({ id: session.body.user.id, email });
65
+ });
66
+ {{/if}}
67
+
43
68
  afterEach(async () => {
44
69
  await app?.close();
45
70
  });
@@ -1,13 +1,10 @@
1
1
  import { Module } from "@nestjs/common";
2
2
  import { GraphQLModule } from "@nestjs/graphql";
3
3
  import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
4
- import { join } from "path";
4
+ import type { IncomingMessage } from "node:http";
5
+ import path from "node:path";
5
6
  import { env } from "@repo/env/api-gateway";
6
- {{#if (eq httpProvider "fastify")}}
7
- // WARNING: If you use @as-integrations/fastify with multiple GraphQL endpoints
8
- // in a single application, make sure to enable the disableHealthCheck setting
9
- // in the GraphQLModule configuration below.
10
- {{/if}}
7
+ import { HealthResolver } from "./resolvers/health.resolver.js";
11
8
  {{#if hasTodoExample}}
12
9
  import { TodoResolver } from "./resolvers/todo.resolver.js";
13
10
  {{/if}}
@@ -16,15 +13,14 @@ import { TodoResolver } from "./resolvers/todo.resolver.js";
16
13
  imports: [
17
14
  GraphQLModule.forRoot<ApolloDriverConfig>({
18
15
  driver: ApolloDriver,
19
- autoSchemaFile: join(process.cwd(), "apps/api-gateway/src/schema.gql"),
16
+ autoSchemaFile: path.join(process.cwd(), "apps/api-gateway/src/schema.gql"),
20
17
  graphiql: env.GRAPHQL_PLAYGROUND,
18
+ context: ({ req, res }: { req: IncomingMessage; res: unknown }) => ({ req, res }),
21
19
  sortSchema: true,
22
- {{#if (eq httpProvider "fastify")}}
23
- disableHealthCheck: true,
24
- {{/if}}
25
20
  }),
26
21
  ],
27
22
  providers: [
23
+ HealthResolver,
28
24
  {{#if hasTodoExample}}
29
25
  TodoResolver,
30
26
  {{/if}}
@@ -0,0 +1,14 @@
1
+ import { Query, Resolver } from "@nestjs/graphql";
2
+ {{#if isBetterAuth}}
3
+ import { AllowAnonymous } from "@thallesp/nestjs-better-auth";
4
+ {{/if}}
5
+ {{#if isBetterAuth}}
6
+ @AllowAnonymous()
7
+ {{/if}}
8
+ @Resolver()
9
+ export class HealthResolver {
10
+ @Query(() => String)
11
+ hello(): string {
12
+ return "Hello GraphQL!";
13
+ }
14
+ }