@nest-arch/tui 0.2.8 → 0.4.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 (70) hide show
  1. package/README.md +20 -42
  2. package/dist/cli.js +1 -1
  3. package/dist/constants-C3xmDWeK.js +1 -0
  4. package/dist/index.js +297 -84
  5. package/dist/templates/base/.env.example.hbs +4 -2
  6. package/dist/templates/base/.env.hbs +4 -2
  7. package/dist/templates/base/.gitignore.hbs +2 -1
  8. package/dist/templates/base/.yarnrc.yml.hbs +0 -5
  9. package/dist/templates/base/README.md.hbs +33 -5
  10. package/dist/templates/base/nest-cli.json.hbs +2 -1
  11. package/dist/templates/base/pnpm-workspace.yaml.hbs +9 -0
  12. package/dist/templates/base/prisma.config.ts.hbs +32 -6
  13. package/dist/templates/base/src/app.controller.spec.ts.hbs +3 -11
  14. package/dist/templates/base/src/app.controller.ts.hbs +2 -2
  15. package/dist/templates/base/src/app.module.ts.hbs +8 -4
  16. package/dist/templates/base/src/app.router.ts.hbs +2 -2
  17. package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +7 -0
  18. package/dist/templates/base/src/database/adapters/mongo/prisma-mongo.adapter.ts.hbs +25 -1
  19. package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +17 -12
  20. package/dist/templates/base/src/database/adapters/sql/drizzle-sql.adapter.ts.hbs +9 -0
  21. package/dist/templates/base/src/database/adapters/sql/prisma-sql.adapter.ts.hbs +7 -4
  22. package/dist/templates/base/src/database/adapters/sql/raw-sql.adapter.ts.hbs +37 -0
  23. package/dist/templates/base/src/database/adapters/sql/typeorm-sql.adapter.ts.hbs +8 -3
  24. package/dist/templates/base/src/database/db.module.ts.hbs +32 -35
  25. package/dist/templates/base/src/database/db.service.ts.hbs +40 -6
  26. package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +12 -7
  27. package/dist/templates/base/src/database/index.ts.hbs +1 -0
  28. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +47 -5
  29. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -4
  30. package/dist/templates/base/src/libs/trpc/error.handler.ts.hbs +1 -1
  31. package/dist/templates/base/src/main.ts.hbs +21 -6
  32. package/dist/templates/base/test/app.e2e-spec.ts.hbs +208 -6
  33. package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +74 -0
  34. package/dist/templates/base/tsconfig.build.json.hbs +2 -0
  35. package/dist/templates/base/vitest.config.e2e.ts.hbs +4 -3
  36. package/dist/templates/base/vitest.config.ts.hbs +4 -3
  37. package/dist/templates/base/yarn.lock.hbs +1 -0
  38. package/dist/templates/extras/docker/Dockerfile.hbs +4 -1
  39. package/dist/templates/extras/swagger/create-docs.ts.hbs +4 -19
  40. package/dist/templates/feature/graphql/src/graphql/graphql.module.ts.hbs +8 -1
  41. package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +4 -0
  42. package/dist/templates/feature/graphql/src/graphql/orm/drizzle/drizzle-graphql.module.ts.hbs +4 -3
  43. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +51 -0
  44. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +3 -1
  45. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.module.ts.hbs +1 -3
  46. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +98 -32
  47. package/dist/templates/monorepo/apps/api-gateway/package.json.hbs +0 -1
  48. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.spec.ts.hbs +1 -1
  49. package/dist/templates/monorepo/apps/api-gateway/test/app.e2e-spec.ts.hbs +20 -1
  50. package/dist/templates/monorepo/apps/api-gateway/tsconfig.build.json.hbs +2 -1
  51. package/dist/templates/monorepo/apps/api-gateway/vitest.config.e2e.ts.hbs +3 -2
  52. package/dist/templates/monorepo/apps/api-gateway/vitest.config.ts.hbs +3 -2
  53. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +9 -0
  54. package/dist/templates/monorepo/packages/env/src/api-gateway.ts.hbs +9 -3
  55. package/dist/templates/monorepo/root/.gitignore.hbs +1 -0
  56. package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +9 -0
  57. package/dist/templates/monorepo/root/tsconfig.json.hbs +1 -1
  58. package/dist/templates/monorepo/root/turbo.json.hbs +2 -0
  59. package/dist/templates/single-project/architecture/nest-api/.env.hbs +3 -1
  60. package/dist/templates/single-project/architecture/nest-api/package.json.hbs +0 -1
  61. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +20 -5
  62. package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +18 -0
  63. package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
  64. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +14 -1
  65. package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +4 -0
  66. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +13 -2
  67. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +64 -0
  68. package/dist/templates/single-project/shared/config/env.ts.hbs +27 -3
  69. package/package.json +5 -2
  70. package/dist/constants-Cgtoxhmd.js +0 -1
@@ -1,11 +1,12 @@
1
1
  import { defineConfig } from "vitest/config";
2
- import tsconfigPaths from "vite-tsconfig-paths";
3
2
 
4
3
  export default defineConfig({
5
- plugins: [tsconfigPaths()],
4
+ resolve: {
5
+ tsconfigPaths: true,
6
+ },
6
7
  test: {
8
+ include: ["**/*.spec.ts"],
7
9
  globals: true,
8
10
  root: "./",
9
- include: ["**/*.spec.ts"],
10
11
  },
11
12
  });
@@ -0,0 +1 @@
1
+ # This lockfile marks the generated application as an independent Yarn project.
@@ -18,5 +18,8 @@ COPY . .
18
18
  RUN {{packageManager}} run build
19
19
 
20
20
  EXPOSE 3000
21
+ {{#if (includes microservices "grpc")}}
22
+ EXPOSE 5000
23
+ {{/if}}
21
24
 
22
- CMD ["{{packageManager}}", "run", "start:prod"]
25
+ CMD ["{{packageManager}}", "run", "start:prod"]
@@ -1,7 +1,3 @@
1
- // @ts-nocheck
2
- /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, import/no-unresolved */
3
- /* oxlint-disable import/no-unresolved */
4
- // oxlint-disable no-unused-vars
5
1
 
6
2
  import type { INestApplication } from "@nestjs/common";
7
3
  import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
@@ -80,7 +76,8 @@ async function applyZodCleanup(
80
76
  options?: NonNullable<DocsIntegrations["zod"]>
81
77
  ) {
82
78
  try {
83
- const { cleanupOpenApiDoc } = await import("nestjs-zod");
79
+ const packageName = "nestjs-zod";
80
+ const { cleanupOpenApiDoc } = await import(packageName);
84
81
  if (typeof cleanupOpenApiDoc !== "function") {
85
82
  return baseSpec;
86
83
  }
@@ -143,7 +140,8 @@ async function applyTrpcSpec(options?: NonNullable<DocsIntegrations["trpc"]>) {
143
140
  }
144
141
 
145
142
  try {
146
- const { generateOpenAPIDocument } = await import("@trpc/openapi");
143
+ const packageName = "@trpc/openapi";
144
+ const { generateOpenAPIDocument } = await import(packageName);
147
145
 
148
146
  if (typeof generateOpenAPIDocument !== "function") {
149
147
  return null;
@@ -174,19 +172,6 @@ async function applyTrpcSpec(options?: NonNullable<DocsIntegrations["trpc"]>) {
174
172
  }
175
173
  }
176
174
 
177
- function createEmptyBaseSpec(options: CreateDocsOptions): OpenAPIObject {
178
- return {
179
- info: {
180
- description: options.description ?? "Generated with Nest Arch",
181
- title: options.title ?? "NestJS API",
182
- version: options.version ?? "1.0.0",
183
- },
184
- openapi: "3.1.0",
185
- paths: {},
186
- tags: [],
187
- } as OpenAPIObject;
188
- }
189
-
190
175
  export async function createDocs(
191
176
  app: INestApplication,
192
177
  options: CreateDocsOptions = {}
@@ -1,5 +1,5 @@
1
1
  import { Module } from "@nestjs/common";
2
- import { GraphQLModule } from "@nestjs/graphql";
2
+ import { Query, Resolver, GraphQLModule } from "@nestjs/graphql";
3
3
  import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
4
4
  import { join } from "path";
5
5
  import { env } from "../config/env.js";
@@ -12,6 +12,12 @@ import { env } from "../config/env.js";
12
12
  import { TodoResolver } from "./resolvers/todo.resolver.js";
13
13
  {{/if}}
14
14
 
15
+ @Resolver()
16
+ class HealthResolver {
17
+ @Query(() => String)
18
+ hello(): string { return "Hello GraphQL!"; }
19
+ }
20
+
15
21
  @Module({
16
22
  imports: [
17
23
  GraphQLModule.forRoot<ApolloDriverConfig>({
@@ -25,6 +31,7 @@ import { TodoResolver } from "./resolvers/todo.resolver.js";
25
31
  }),
26
32
  ],
27
33
  providers: [
34
+ HealthResolver,
28
35
  {{#if hasTodoExample}}
29
36
  TodoResolver,
30
37
  {{/if}}
@@ -1,8 +1,10 @@
1
1
  import { Field, ID, ObjectType } from "@nestjs/graphql";
2
2
 
3
3
  import { BaseModel } from "./base.model.js";
4
+ {{#if hasAuth}}
4
5
  // oxlint-disable-next-line import/no-cycle
5
6
  import { User } from "./user.model.js";
7
+ {{/if}}
6
8
 
7
9
  @ObjectType()
8
10
  export class Todo extends BaseModel {
@@ -15,9 +17,11 @@ export class Todo extends BaseModel {
15
17
  @Field()
16
18
  completed: boolean;
17
19
 
20
+ {{#if hasAuth}}
18
21
  @Field(() => ID, { nullable: true })
19
22
  authorId: string | null;
20
23
 
21
24
  @Field(() => User, { nullable: true })
22
25
  author: User | null;
26
+ {{/if}}
23
27
  }
@@ -3,6 +3,7 @@ import { buildSchema } from "drizzle-graphql";
3
3
  {{#if isMonorepo}}
4
4
  import { db } from "@repo/db";
5
5
  {{else}}
6
+ import { env } from "../../../config/env.js";
6
7
  {{#if hasPostgres}}
7
8
  import { drizzle } from "drizzle-orm/node-postgres";
8
9
  import { Pool } from "pg";
@@ -20,13 +21,13 @@ import * as dbSchema from "../database/orm/drizzle/schema.js";
20
21
 
21
22
  const schema = buildSchema({{#if isMonorepo}}db{{else}}
22
23
  {{#if hasPostgres}}
23
- drizzle(new Pool({ connectionString: process.env.DATABASE_URL }), { schema: dbSchema })
24
+ drizzle(new Pool({ connectionString: env.POSTGRES_URL }), { schema: dbSchema })
24
25
  {{/if}}
25
26
  {{#if hasMysql}}
26
- drizzle(mysql.createPool(process.env.DATABASE_URL), { schema: dbSchema })
27
+ drizzle(mysql.createPool(env.MYSQL_URL), { schema: dbSchema })
27
28
  {{/if}}
28
29
  {{#if hasSqlite}}
29
- drizzle(new Database(process.env.DATABASE_URL), { schema: dbSchema })
30
+ drizzle(new Database(env.SQLITE_PATH), { schema: dbSchema })
30
31
  {{/if}}
31
32
  {{/if}});
32
33
 
@@ -1,3 +1,43 @@
1
+ {{#if (and usePrisma (eq auth "none"))}}
2
+ import { BadRequestException, NotFoundException } from "@nestjs/common";
3
+ import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
4
+ import { DbService } from "../../database/db.service.js";
5
+ import { CreateTodoInput } from "../dto/create-todo.input.js";
6
+ import { UpdateTodoInput } from "../dto/update-todo.input.js";
7
+ import { Todo } from "../models/todo.model.js";
8
+
9
+ @Resolver(() => Todo)
10
+ export class TodoResolver {
11
+ constructor(private readonly db: DbService) {}
12
+
13
+ @Query(() => [Todo])
14
+ todos() {
15
+ return this.db.prisma.todo.findMany({ orderBy: { createdAt: "asc" }, take: 100 });
16
+ }
17
+
18
+ @Mutation(() => Todo)
19
+ createTodo(@Args("input") input: CreateTodoInput) {
20
+ if (!input.title.trim()) throw new BadRequestException("Title must not be empty");
21
+ return this.db.prisma.todo.create({ data: input });
22
+ }
23
+
24
+ @Mutation(() => Todo)
25
+ async updateTodo(@Args("input") input: UpdateTodoInput) {
26
+ const { id, ...data } = input;
27
+ if (data.title !== undefined && !data.title.trim()) throw new BadRequestException("Title must not be empty");
28
+ const result = await this.db.prisma.todo.updateManyAndReturn({ where: { id }, data });
29
+ if (!result[0]) throw new NotFoundException("Todo not found");
30
+ return result[0];
31
+ }
32
+
33
+ @Mutation(() => Boolean)
34
+ async deleteTodo(@Args("id") id: string): Promise<boolean> {
35
+ const result = await this.db.prisma.todo.deleteMany({ where: { id } });
36
+ if (result.count === 0) throw new NotFoundException("Todo not found");
37
+ return true;
38
+ }
39
+ }
40
+ {{else}}
1
41
  {{#if (and hasAuth hasDatabase)}}
2
42
  import { UseGuards } from "@nestjs/common";
3
43
  {{/if}}
@@ -39,6 +79,8 @@ export class TodoResolver {
39
79
  const builder = new GraphRelationBuilder(this.db.getDataSource());
40
80
  const relations = builder.buildForQuery(Todo, info, "todos");
41
81
  return this.db.getRepository(Todo).find({ relations });
82
+ {{else if isPrisma8}}
83
+ return ((this.db.prisma.orm.public as any).Todo).all();
42
84
  {{else if usePrisma}}
43
85
  return this.db.prisma.todo.findMany();
44
86
  {{else if useDrizzle}}
@@ -63,6 +105,8 @@ export class TodoResolver {
63
105
  where: { id: saved.id },
64
106
  relations,
65
107
  });
108
+ {{else if isPrisma8}}
109
+ return ((this.db.prisma.orm.public as any).Todo).create({ ...input, authorId: user.id });
66
110
  {{else if usePrisma}}
67
111
  return this.db.prisma.todo.create({
68
112
  data: { ...input, authorId: user.id },
@@ -87,6 +131,8 @@ export class TodoResolver {
87
131
  where: { id },
88
132
  relations,
89
133
  });
134
+ {{else if isPrisma8}}
135
+ return ((this.db.prisma.orm.public as any).Todo).where({ id }).update(data);
90
136
  {{else if usePrisma}}
91
137
  return this.db.prisma.todo.update({ where: { id }, data });
92
138
  {{else if useDrizzle}}
@@ -107,6 +153,9 @@ export class TodoResolver {
107
153
  {{#if useTypeorm}}
108
154
  const result = await this.db.getRepository(Todo).delete(id);
109
155
  return (result.affected ?? 0) > 0;
156
+ {{else if isPrisma8}}
157
+ await ((this.db.prisma.orm.public as any).Todo).where({ id }).delete();
158
+ return true;
110
159
  {{else if usePrisma}}
111
160
  await this.db.prisma.todo.delete({ where: { id } });
112
161
  return true;
@@ -118,3 +167,5 @@ export class TodoResolver {
118
167
  {{/if}}
119
168
  }{{/if}}
120
169
  }
170
+
171
+ {{/if}}
@@ -16,7 +16,9 @@ import {
16
16
  ApiOkResponse,
17
17
  ApiOperation,
18
18
  ApiTags,
19
+ {{#if (or isPassportAuth isBetterAuth)}}
19
20
  ApiUnauthorizedResponse,
21
+ {{/if}}
20
22
  } from "@nestjs/swagger";
21
23
  {{/if}}
22
24
  {{#if isPassportAuth}}
@@ -164,7 +166,7 @@ export class TodoController {
164
166
  return this.todoService.delete(id, session.user.id);
165
167
  }
166
168
  {{else}}
167
- async delete(@Param("id") id: string) {
169
+ delete(@Param("id") id: string) {
168
170
  return this.todoService.delete(id);
169
171
  }
170
172
  {{/if}}
@@ -11,14 +11,12 @@ import { TodoRouter } from "./todo.router.js";
11
11
  {{#if hasRest}}
12
12
  controllers: [TodoController],
13
13
  {{/if}}
14
+ exports: [TodoService],
14
15
  providers: [
15
16
  TodoService,
16
17
  {{#unless hasRest}}
17
18
  TodoRouter,
18
19
  {{/unless}}
19
20
  ],
20
- {{#if hasRest}}
21
- exports: [TodoService],
22
- {{/if}}
23
21
  })
24
22
  export class TodoModule {}
@@ -1,3 +1,37 @@
1
+ {{#if (and hasMongodb useRawSql)}}
2
+ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
3
+ import { ObjectId, type WithId } from "mongodb";
4
+ import { DbService } from "../../database/db.service.js";
5
+ import { CreateTodoDto } from "./dto/create-todo.dto.js";
6
+ import { UpdateTodoDto } from "./dto/update-todo.dto.js";
7
+ interface TodoDocument { title: string; completed: boolean; description?: string; }
8
+ const serialize = ({ _id, ...todo }: WithId<TodoDocument>) => ({ ...todo, id: _id.toHexString() });
9
+ function objectId(id: string): ObjectId {
10
+ if (!/^[a-f0-9]{24}$/iu.test(id)) throw new BadRequestException("Invalid todo identifier");
11
+ return new ObjectId(id);
12
+ }
13
+ @Injectable()
14
+ export class TodoService {
15
+ constructor(private readonly db: DbService) {}
16
+ private get todos() { return this.db.raw.mongo.collection<TodoDocument>("todos"); }
17
+ async findAll() { return (await this.todos.find().sort({ _id: 1 }).limit(100).toArray()).map(serialize); }
18
+ async create(input: CreateTodoDto) {
19
+ const todo = { title: input.title, completed: input.completed ?? false, ...(input.description === undefined ? {} : { description: input.description }) };
20
+ const result = await this.todos.insertOne(todo);
21
+ return { ...todo, id: result.insertedId.toHexString() };
22
+ }
23
+ async update(id: string, input: UpdateTodoDto) {
24
+ const todo = await this.todos.findOneAndUpdate({ _id: objectId(id) }, { $set: input }, { returnDocument: "after" });
25
+ if (!todo) throw new NotFoundException("Todo not found");
26
+ return serialize(todo);
27
+ }
28
+ async delete(id: string) {
29
+ const result = await this.todos.deleteOne({ _id: objectId(id) });
30
+ if (result.deletedCount === 0) throw new NotFoundException("Todo not found");
31
+ return { message: "Todo deleted successfully" };
32
+ }
33
+ }
34
+ {{else}}
1
35
  import { Injectable } from "@nestjs/common";
2
36
  {{#if hasDatabase}}
3
37
  {{#if isMonorepo}}
@@ -7,8 +41,12 @@ import { DbService } from "../../database/db.service.js";
7
41
  {{/if}}
8
42
  {{#if useDrizzle}}
9
43
  import * as schema from "../../database/orm/drizzle/schema.js";
44
+ {{#if hasAuth}}
45
+ import { and, eq } from "drizzle-orm";
46
+ {{else}}
10
47
  import { eq } from "drizzle-orm";
11
48
  {{/if}}
49
+ {{/if}}
12
50
  {{#if useTypeorm}}
13
51
  import { Todo } from "../../database/entities/todo.entity.js";
14
52
  {{/if}}
@@ -33,83 +71,109 @@ export class TodoService {
33
71
  {{#if hasDatabase}}
34
72
  {{#if hasAuth}}
35
73
  findAll(authorId: string) {
36
- {{#if usePrisma}}
74
+ {{#if isPrisma8}}
75
+ return ((this.db.prisma.orm.public as any).Todo).where({ authorId }).all();
76
+ {{else if usePrisma}}
37
77
  return this.db.prisma.todo.findMany({ where: { authorId } });
38
78
  {{else if useTypeorm}}
39
- return this.db.getRepository(Todo).find({ where: { authorId } });
79
+ return this.db.typeorm.getRepository(Todo).find({ where: { authorId } });
40
80
  {{else if useDrizzle}}
41
- return this.db.select().from(schema.todos).where(eq(schema.todos.authorId, authorId));
81
+ return this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.authorId, authorId));
42
82
  {{/if}}
43
83
  }
44
84
 
45
- create(input: CreateTodoDto, authorId: string) {
46
- {{#if usePrisma}}
47
- return this.db.prisma.todo.create({ data: { ...input, authorId } });
85
+ async create(input: CreateTodoDto, authorId: string) {
86
+ {{#if isPrisma8}}
87
+ return ((this.db.prisma.orm.public as any).Todo).create({ ...input, authorId });
88
+ {{else if usePrisma}}
89
+ const todo = await this.db.prisma.todo.create({ data: { ...input, authorId } });
90
+ return todo;
48
91
  {{else if useTypeorm}}
49
- return this.db.getRepository(Todo).save(this.db.getRepository(Todo).create({ ...input, authorId }));
92
+ const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create({ ...input, authorId }));
93
+ return todo;
50
94
  {{else if useDrizzle}}
51
- return this.db.insert(schema.todos).values({ ...input, authorId }).returning();
95
+ return this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).returning();
52
96
  {{/if}}
53
97
  }
54
98
 
55
- update(id: string, input: UpdateTodoDto, authorId: string) {
56
- {{#if usePrisma}}
57
- return this.db.prisma.todo.update({ data: input, where: { authorId, id } });
99
+ async update(id: string, input: UpdateTodoDto, authorId: string) {
100
+ {{#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 } });
104
+ return result;
58
105
  {{else if useTypeorm}}
59
- return this.db.getRepository(Todo).update({ authorId, id }, input);
106
+ const result = await this.db.typeorm.getRepository(Todo).update({ authorId, id }, input);
107
+ return result;
60
108
  {{else if useDrizzle}}
61
- return this.db.update(schema.todos).set(input).where(eq(schema.todos.authorId, authorId) && eq(schema.todos.id, id)).returning();
109
+ return this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
62
110
  {{/if}}
63
111
  }
64
112
 
65
113
  async delete(id: string, authorId: string) {
66
- {{#if usePrisma}}
114
+ {{#if isPrisma8}}
115
+ await ((this.db.prisma.orm.public as any).Todo).where({ authorId, id }).delete();
116
+ {{else if usePrisma}}
67
117
  await this.db.prisma.todo.delete({ where: { authorId, id } });
68
118
  {{else if useTypeorm}}
69
- await this.db.getRepository(Todo).delete({ authorId, id });
119
+ await this.db.typeorm.getRepository(Todo).delete({ authorId, id });
70
120
  {{else if useDrizzle}}
71
- await this.db.delete(schema.todos).where(eq(schema.todos.authorId, authorId) && eq(schema.todos.id, id));
121
+ await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
72
122
  {{/if}}
73
123
  return { message: "Todo deleted successfully" };
74
124
  }
75
125
  {{else}}
76
126
  findAll() {
77
- {{#if usePrisma}}
127
+ {{#if isPrisma8}}
128
+ return ((this.db.prisma.orm.public as any).Todo).all();
129
+ {{else if usePrisma}}
78
130
  return this.db.prisma.todo.findMany();
79
131
  {{else if useTypeorm}}
80
- return this.db.getRepository(Todo).find();
132
+ return this.db.typeorm.getRepository(Todo).find();
81
133
  {{else if useDrizzle}}
82
- return this.db.select().from(schema.todos);
134
+ return this.db.drizzle.select().from(schema.todos);
83
135
  {{/if}}
84
136
  }
85
137
 
86
- create(input: CreateTodoDto) {
87
- {{#if usePrisma}}
88
- return this.db.prisma.todo.create({ data: input });
138
+ async create(input: CreateTodoDto) {
139
+ {{#if isPrisma8}}
140
+ return ((this.db.prisma.orm.public as any).Todo).create(input);
141
+ {{else if usePrisma}}
142
+ const todo = await this.db.prisma.todo.create({ data: input });
143
+ return todo;
89
144
  {{else if useTypeorm}}
90
- return this.db.getRepository(Todo).save(this.db.getRepository(Todo).create(input));
145
+ const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create(input));
146
+ return todo;
91
147
  {{else if useDrizzle}}
92
- return this.db.insert(schema.todos).values(input).returning();
148
+ const [todo] = await this.db.drizzle.insert(schema.todos).values(input).returning();
149
+ return todo;
93
150
  {{/if}}
94
151
  }
95
152
 
96
- update(id: string, input: UpdateTodoDto) {
97
- {{#if usePrisma}}
98
- return this.db.prisma.todo.update({ data: input, where: { id } });
153
+ async update(id: string, input: UpdateTodoDto) {
154
+ {{#if isPrisma8}}
155
+ return ((this.db.prisma.orm.public as any).Todo).where({ id }).update(input);
156
+ {{else if usePrisma}}
157
+ const result = await this.db.prisma.todo.update({ data: input, where: { id } });
158
+ return result;
99
159
  {{else if useTypeorm}}
100
- return this.db.getRepository(Todo).update(id, input);
160
+ const result = await this.db.typeorm.getRepository(Todo).update(id, input);
161
+ return result;
101
162
  {{else if useDrizzle}}
102
- return this.db.update(schema.todos).set(input).where(eq(schema.todos.id, id)).returning();
163
+ const [todo] = await this.db.drizzle.update(schema.todos).set(input).where(eq(schema.todos.id, Number(id))).returning();
164
+ return todo;
103
165
  {{/if}}
104
166
  }
105
167
 
106
168
  async delete(id: string) {
107
- {{#if usePrisma}}
169
+ {{#if isPrisma8}}
170
+ await ((this.db.prisma.orm.public as any).Todo).where({ id }).delete();
171
+ {{else if usePrisma}}
108
172
  await this.db.prisma.todo.delete({ where: { id } });
109
173
  {{else if useTypeorm}}
110
- await this.db.getRepository(Todo).delete(id);
174
+ await this.db.typeorm.getRepository(Todo).delete(id);
111
175
  {{else if useDrizzle}}
112
- await this.db.delete(schema.todos).where(eq(schema.todos.id, id));
176
+ await this.db.drizzle.delete(schema.todos).where(eq(schema.todos.id, Number(id)));
113
177
  {{/if}}
114
178
  return { message: "Todo deleted successfully" };
115
179
  }
@@ -143,3 +207,5 @@ export class TodoService {
143
207
  }
144
208
  {{/if}}
145
209
  }
210
+
211
+ {{/if}}
@@ -45,7 +45,6 @@
45
45
  "source-map-support": "^0.5.21",
46
46
  "supertest": "^7.0.0",
47
47
  "typescript": "catalog:",
48
- "vite-tsconfig-paths": "^5.1.4",
49
48
  {{#if hasUltracite}}"ultracite": "7.8.0",{{/if}}
50
49
  "vitest": "^4.1.2"
51
50
  }
@@ -1,7 +1,7 @@
1
1
  // oxlint-disable vitest/require-mock-type-parameters
2
2
  import type { TestingModule } from "@nestjs/testing";
3
3
  import { Test } from "@nestjs/testing";
4
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { beforeEach, describe, expect, it{{#if (or hasDatabase hasOrm)}}, vi{{/if}} } from "vitest";
5
5
 
6
6
  import { AppController } from "./app.controller.js";
7
7
  import { AppService } from "./app.service.js";
@@ -1,6 +1,10 @@
1
1
  import { INestApplication } from "@nestjs/common";
2
2
  import { Test, TestingModule } from "@nestjs/testing";
3
+ {{#if (eq httpProvider "fastify")}}
4
+ import { FastifyAdapter } from "@nestjs/platform-fastify";
5
+ {{/if}}
3
6
  import request from "supertest";
7
+ import { afterEach, beforeEach, describe, it } from "vitest";
4
8
  import type { App } from "supertest/types";
5
9
 
6
10
  import { AppModule } from "./../src/app.module.js";
@@ -13,15 +17,30 @@ describe("AppController (e2e)", () => {
13
17
  imports: [AppModule],
14
18
  }).compile();
15
19
 
20
+ {{#if (eq httpProvider "fastify")}}
21
+ app = moduleFixture.createNestApplication(new FastifyAdapter());
22
+ {{else}}
16
23
  app = moduleFixture.createNestApplication();
24
+ {{/if}}
17
25
  await app.init();
26
+ {{#if (eq httpProvider "fastify")}}
27
+ await app.getHttpAdapter().getInstance().ready();
28
+ {{/if}}
18
29
  });
19
30
 
20
31
  it("/ (GET)", () => {
21
32
  return request(app.getHttpServer()).get("/").expect(200).expect("Hello World!");
22
33
  });
23
34
 
35
+ it("returns 404 for an unknown route", async () => {
36
+ await request(app.getHttpServer()).get("/__missing_route__").expect(404);
37
+ });
38
+
39
+ it("rejects unsupported methods", async () => {
40
+ await request(app.getHttpServer()).post("/").send({ unexpected: true }).expect(404);
41
+ });
42
+
24
43
  afterEach(async () => {
25
- await app.close();
44
+ await app?.close();
26
45
  });
27
46
  });
@@ -2,7 +2,8 @@
2
2
  "$schema": "https://json.schemastore.org/tsconfig",
3
3
  "extends": "./tsconfig.json",
4
4
  "compilerOptions": {
5
- "outDir": "./dist"
5
+ "outDir": "./dist",
6
+ "rootDir": "./src"
6
7
  },
7
8
  "include": ["src/**/*"],
8
9
  "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
@@ -1,8 +1,9 @@
1
1
  import { defineConfig } from "vitest/config";
2
- import tsconfigPaths from "vite-tsconfig-paths";
3
2
 
4
3
  export default defineConfig({
5
- plugins: [tsconfigPaths()],
4
+ resolve: {
5
+ tsconfigPaths: true,
6
+ },
6
7
  test: {
7
8
  globals: true,
8
9
  root: "./",
@@ -1,8 +1,9 @@
1
1
  import { defineConfig } from "vitest/config";
2
- import tsconfigPaths from "vite-tsconfig-paths";
3
2
 
4
3
  export default defineConfig({
5
- plugins: [tsconfigPaths()],
4
+ resolve: {
5
+ tsconfigPaths: true,
6
+ },
6
7
  test: {
7
8
  globals: true,
8
9
  root: "./",
@@ -35,6 +35,8 @@ export class TodoResolver {
35
35
  const builder = new GraphRelationBuilder(this.db.getDataSource());
36
36
  const relations = builder.buildForQuery(Todo, info, "todos");
37
37
  return this.db.getRepository(Todo).find({ relations });
38
+ {{else if isPrisma8}}
39
+ return ((this.db.prisma.orm.public as any).Todo).all();
38
40
  {{else if usePrisma}}
39
41
  return this.db.prisma.todo.findMany();
40
42
  {{else if useDrizzle}}
@@ -59,6 +61,8 @@ export class TodoResolver {
59
61
  where: { id: saved.id },
60
62
  relations,
61
63
  });
64
+ {{else if isPrisma8}}
65
+ return ((this.db.prisma.orm.public as any).Todo).create({ ...input, authorId: user.id });
62
66
  {{else if usePrisma}}
63
67
  return this.db.prisma.todo.create({
64
68
  data: { ...input, authorId: user.id },
@@ -83,6 +87,8 @@ export class TodoResolver {
83
87
  where: { id },
84
88
  relations,
85
89
  });
90
+ {{else if isPrisma8}}
91
+ return ((this.db.prisma.orm.public as any).Todo).where({ id }).update(data);
86
92
  {{else if usePrisma}}
87
93
  return this.db.prisma.todo.update({ where: { id }, data });
88
94
  {{else if useDrizzle}}
@@ -103,6 +109,9 @@ export class TodoResolver {
103
109
  {{#if useTypeorm}}
104
110
  const result = await this.db.getRepository(Todo).delete(id);
105
111
  return (result.affected ?? 0) > 0;
112
+ {{else if isPrisma8}}
113
+ await ((this.db.prisma.orm.public as any).Todo).where({ id }).delete();
114
+ return true;
106
115
  {{else if usePrisma}}
107
116
  await this.db.prisma.todo.delete({ where: { id } });
108
117
  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,7 +31,7 @@ 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
36
  {{#if hasPostgres}}
37
37
  POSTGRES_URL: z.url(),
@@ -51,9 +51,15 @@ export const env = createEnv({
51
51
  .transform((val) => val === "true")
52
52
  .default(true),
53
53
  {{/if}}
54
- {{#if (and (eq auth "passport") hasDatabase)}}
54
+ {{#if (eq auth "passport")}}
55
55
  JWT_ACCESS_SECRET: z.string().min(32),
56
+ {{#if hasDatabase}}
56
57
  JWT_REFRESH_SECRET: z.string().min(32),
57
58
  {{/if}}
59
+ {{/if}}
60
+ {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
61
+ BETTER_AUTH_SECRET: z.string().min(32),
62
+ BETTER_AUTH_URL: z.url(),
63
+ {{/if}}
58
64
  },
59
65
  });
@@ -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