@nest-arch/tui 0.4.6 → 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.
- package/README.md +31 -0
- package/dist/cli.js +2 -1
- package/dist/constants-CUTbfu9C.js +1 -0
- package/dist/index.js +6 -405
- package/dist/nonInteractive-C1KZ0b_6.js +5 -0
- package/dist/nonInteractiveOptions-CIaoLAWg.js +24 -0
- package/dist/projectGeneratorService-CeWJK-HA.js +420 -0
- package/dist/projectName-CcE90t2V.js +1 -0
- package/dist/templates/addons/husky/pre-commit.hbs +1 -1
- package/dist/templates/base/.env.example.hbs +30 -2
- package/dist/templates/base/.env.hbs +7 -2
- package/dist/templates/base/README.md.hbs +11 -2
- package/dist/templates/base/pnpm-workspace.yaml.hbs +9 -4
- package/dist/templates/base/prisma.config.ts.hbs +6 -1
- package/dist/templates/base/src/app.controller.spec.ts.hbs +6 -0
- package/dist/templates/base/src/app.module.ts.hbs +6 -23
- package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +5 -5
- package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +6 -4
- package/dist/templates/base/src/database/adapters/mongo/typeorm-mongo.adapter.ts.hbs +27 -0
- package/dist/templates/base/src/database/db.module.ts.hbs +33 -13
- package/dist/templates/base/src/database/db.service.ts.hbs +12 -4
- package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +17 -5
- package/dist/templates/base/src/database/index.ts.hbs +2 -0
- package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +80 -20
- package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +34 -12
- package/dist/templates/base/src/libs/middlewares/graphql-throttler.guard.ts.hbs +22 -0
- package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +2 -1
- package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +2 -2
- package/dist/templates/base/src/main.ts.hbs +2 -2
- package/dist/templates/base/test/app.e2e-spec.ts.hbs +378 -202
- package/dist/templates/extras/docker/Dockerfile.hbs +9 -2
- package/dist/templates/extras/health/src/health/health.controller.ts.hbs +12 -1
- package/dist/templates/extras/swagger/create-docs.ts.hbs +6 -1
- package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +1 -1
- package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +1 -1
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +80 -7
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +4 -4
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +314 -34
- package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +2 -2
- package/dist/templates/monorepo/root/README.md.hbs +8 -13
- package/dist/templates/single-project/architecture/nest-api/.env.hbs +6 -2
- package/dist/templates/single-project/architecture/nest-api/pnpm-workspace.yaml.hbs +6 -0
- package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +2 -2
- package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +2 -2
- package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +30 -1
- package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +26 -2
- package/dist/templates/single-project/architecture/nest-microservice/src/app.module.ts.hbs +61 -1
- package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +16 -4
- package/dist/templates/single-project/architecture/nest-microservice/src/http-throttler.guard.ts.hbs +13 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +26 -7
- package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +7 -87
- package/dist/templates/single-project/shared/config/env.ts.hbs +14 -14
- package/package.json +6 -5
- package/dist/constants-DVhgS48N.js +0 -1
|
@@ -1,39 +1,148 @@
|
|
|
1
|
+
{{#if (and hasMongodb useTypeorm)}}
|
|
2
|
+
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
|
3
|
+
import { ObjectId } from "mongodb";
|
|
4
|
+
import type { Document, WithId } from "mongodb";
|
|
5
|
+
|
|
6
|
+
import { DbService } from "../../database/db.service.js";
|
|
7
|
+
import { CreateTodoDto } from "./dto/create-todo.dto.js";
|
|
8
|
+
import { UpdateTodoDto } from "./dto/update-todo.dto.js";
|
|
9
|
+
|
|
10
|
+
interface TodoDocument extends Document {
|
|
11
|
+
completed: boolean;
|
|
12
|
+
createdAt: Date;
|
|
13
|
+
description?: string;
|
|
14
|
+
title: string;
|
|
15
|
+
updatedAt: Date;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const serialize = ({ _id, ...todo }: WithId<TodoDocument>) => ({
|
|
19
|
+
...todo,
|
|
20
|
+
id: _id.toHexString(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function objectId(id: string): ObjectId {
|
|
24
|
+
if (!/^[a-f0-9]{24}$/iu.test(id)) {
|
|
25
|
+
throw new BadRequestException("Invalid todo identifier");
|
|
26
|
+
}
|
|
27
|
+
return new ObjectId(id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Injectable()
|
|
31
|
+
export class TodoService {
|
|
32
|
+
constructor(private readonly db: DbService) {}
|
|
33
|
+
|
|
34
|
+
private get todos() {
|
|
35
|
+
return this.db.mongo.collection<TodoDocument>("todos");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async findAll() {
|
|
39
|
+
const todos = await this.todos.find().sort({ _id: 1 }).limit(100).toArray();
|
|
40
|
+
return todos.map(serialize);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async create(input: CreateTodoDto) {
|
|
44
|
+
const now = new Date();
|
|
45
|
+
const todo = {
|
|
46
|
+
completed: input.completed ?? false,
|
|
47
|
+
createdAt: now,
|
|
48
|
+
...(input.description === undefined ? {} : { description: input.description }),
|
|
49
|
+
title: input.title,
|
|
50
|
+
updatedAt: now,
|
|
51
|
+
};
|
|
52
|
+
const result = await this.todos.insertOne(todo);
|
|
53
|
+
return { ...todo, id: result.insertedId.toHexString() };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async update(id: string, input: UpdateTodoDto) {
|
|
57
|
+
const _id = objectId(id);
|
|
58
|
+
await this.todos.updateOne({ _id }, { $set: { ...input, updatedAt: new Date() } });
|
|
59
|
+
const todo = await this.todos.findOne({ _id });
|
|
60
|
+
if (!todo) {
|
|
61
|
+
throw new NotFoundException("Todo not found");
|
|
62
|
+
}
|
|
63
|
+
return serialize(todo);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async delete(id: string) {
|
|
67
|
+
const result = await this.todos.deleteOne({ _id: objectId(id) });
|
|
68
|
+
if (result.deletedCount === 0) {
|
|
69
|
+
throw new NotFoundException("Todo not found");
|
|
70
|
+
}
|
|
71
|
+
return { message: "Todo deleted successfully" };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
{{else}}
|
|
1
75
|
{{#if (and hasMongodb useRawSql)}}
|
|
2
76
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
|
3
|
-
import { ObjectId
|
|
77
|
+
import { ObjectId } from "mongodb";
|
|
78
|
+
import type { Document, WithId } from "mongodb";
|
|
79
|
+
|
|
4
80
|
import { DbService } from "../../database/db.service.js";
|
|
5
81
|
import { CreateTodoDto } from "./dto/create-todo.dto.js";
|
|
6
82
|
import { UpdateTodoDto } from "./dto/update-todo.dto.js";
|
|
7
|
-
|
|
8
|
-
|
|
83
|
+
|
|
84
|
+
interface TodoDocument extends Document {
|
|
85
|
+
completed: boolean;
|
|
86
|
+
description?: string;
|
|
87
|
+
title: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const serialize = ({ _id, ...todo }: WithId<TodoDocument>) => ({
|
|
91
|
+
...todo,
|
|
92
|
+
id: _id.toHexString(),
|
|
93
|
+
});
|
|
94
|
+
|
|
9
95
|
function objectId(id: string): ObjectId {
|
|
10
|
-
if (!/^[a-f0-9]{24}$/iu.test(id))
|
|
96
|
+
if (!/^[a-f0-9]{24}$/iu.test(id)) {
|
|
97
|
+
throw new BadRequestException("Invalid todo identifier");
|
|
98
|
+
}
|
|
11
99
|
return new ObjectId(id);
|
|
12
100
|
}
|
|
101
|
+
|
|
13
102
|
@Injectable()
|
|
14
103
|
export class TodoService {
|
|
15
104
|
constructor(private readonly db: DbService) {}
|
|
16
|
-
|
|
17
|
-
|
|
105
|
+
|
|
106
|
+
private get todos() {
|
|
107
|
+
return this.db.raw.mongo.collection<TodoDocument>("todos");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async findAll() {
|
|
111
|
+
const todos = await this.todos.find().sort({ _id: 1 }).limit(100).toArray();
|
|
112
|
+
return todos.map(serialize);
|
|
113
|
+
}
|
|
114
|
+
|
|
18
115
|
async create(input: CreateTodoDto) {
|
|
19
|
-
const todo = {
|
|
116
|
+
const todo = {
|
|
117
|
+
completed: input.completed ?? false,
|
|
118
|
+
...(input.description === undefined ? {} : { description: input.description }),
|
|
119
|
+
title: input.title,
|
|
120
|
+
};
|
|
20
121
|
const result = await this.todos.insertOne(todo);
|
|
21
122
|
return { ...todo, id: result.insertedId.toHexString() };
|
|
22
123
|
}
|
|
124
|
+
{{#if isPrisma8}}
|
|
125
|
+
update(id: string, input: UpdateTodoDto) {
|
|
126
|
+
{{else}}
|
|
23
127
|
async update(id: string, input: UpdateTodoDto) {
|
|
128
|
+
{{/if}}
|
|
24
129
|
const todo = await this.todos.findOneAndUpdate({ _id: objectId(id) }, { $set: input }, { returnDocument: "after" });
|
|
25
|
-
if (!todo)
|
|
130
|
+
if (!todo) {
|
|
131
|
+
throw new NotFoundException("Todo not found");
|
|
132
|
+
}
|
|
26
133
|
return serialize(todo);
|
|
27
134
|
}
|
|
28
135
|
async delete(id: string) {
|
|
29
136
|
const result = await this.todos.deleteOne({ _id: objectId(id) });
|
|
30
|
-
if (result.deletedCount === 0)
|
|
137
|
+
if (result.deletedCount === 0) {
|
|
138
|
+
throw new NotFoundException("Todo not found");
|
|
139
|
+
}
|
|
31
140
|
return { message: "Todo deleted successfully" };
|
|
32
141
|
}
|
|
33
142
|
}
|
|
34
143
|
{{else}}
|
|
35
|
-
{{#if (and hasDatabase hasAuth)}}
|
|
36
|
-
import { Injectable, NotFoundException } from "@nestjs/common";
|
|
144
|
+
{{#if (or (and hasTodoExample (not hasDatabase)) hasAuth isPrisma8 (and hasDatabase useTypeorm) (and hasDatabase useDrizzle (or hasMysql hasSqlserver)) (and hasMongodb usePrisma))}}
|
|
145
|
+
import { {{#if (and hasMongodb usePrisma (not isPrisma8))}}BadRequestException, {{/if}}Injectable, NotFoundException{{#if (and hasAuth hasPostgres useRawSql)}}, type OnModuleInit{{/if}} } from "@nestjs/common";
|
|
37
146
|
{{else}}
|
|
38
147
|
import { Injectable } from "@nestjs/common";
|
|
39
148
|
{{/if}}
|
|
@@ -51,34 +160,84 @@ import { and, eq } from "drizzle-orm";
|
|
|
51
160
|
import { eq } from "drizzle-orm";
|
|
52
161
|
{{/if}}
|
|
53
162
|
{{/if}}
|
|
54
|
-
{{#if useTypeorm}}
|
|
163
|
+
{{#if (and useTypeorm (not hasMongodb))}}
|
|
55
164
|
import { Todo } from "../../database/entities/todo.entity.js";
|
|
56
165
|
{{/if}}
|
|
166
|
+
{{#if (and hasAuth hasPostgres useRawSql)}}
|
|
167
|
+
import type { Pool } from "pg";
|
|
168
|
+
{{/if}}
|
|
57
169
|
{{/if}}
|
|
58
170
|
import { CreateTodoDto } from "./dto/create-todo.dto.js";
|
|
59
171
|
import { UpdateTodoDto } from "./dto/update-todo.dto.js";
|
|
172
|
+
{{#if isPrisma8}}
|
|
173
|
+
import type { Models } from "../../prisma/contract.js";
|
|
174
|
+
|
|
175
|
+
type Todo = Models.public_Todo;
|
|
176
|
+
{{/if}}
|
|
60
177
|
|
|
61
178
|
@Injectable()
|
|
179
|
+
{{#if (and hasAuth hasPostgres useRawSql)}}
|
|
180
|
+
export class TodoService implements OnModuleInit {
|
|
181
|
+
{{else}}
|
|
62
182
|
export class TodoService {
|
|
183
|
+
{{/if}}
|
|
184
|
+
{{#if (and hasMongodb usePrisma (not isPrisma8))}}
|
|
185
|
+
private assertMongoId(id: string): void {
|
|
186
|
+
if (!/^[a-f0-9]{24}$/iu.test(id)) {
|
|
187
|
+
throw new BadRequestException("Invalid todo identifier");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
{{/if}}
|
|
63
191
|
{{#if hasDatabase}}
|
|
64
192
|
constructor(private readonly db: DbService) {}
|
|
65
193
|
{{else}}
|
|
66
|
-
private todos:
|
|
194
|
+
private todos: {
|
|
195
|
+
{{#if hasAuth}}
|
|
196
|
+
authorId: string;
|
|
197
|
+
{{/if}}
|
|
67
198
|
completed: boolean;
|
|
68
199
|
description?: string;
|
|
69
200
|
id: string;
|
|
70
201
|
title: string;
|
|
71
|
-
}
|
|
202
|
+
}[] = [];
|
|
72
203
|
private nextId = 1;
|
|
73
204
|
{{/if}}
|
|
74
205
|
|
|
206
|
+
{{#if (and hasAuth hasPostgres useRawSql)}}
|
|
207
|
+
async onModuleInit(): Promise<void> {
|
|
208
|
+
const pool = this.db.raw.sql as Pool;
|
|
209
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS todos (
|
|
210
|
+
id SERIAL PRIMARY KEY,
|
|
211
|
+
title TEXT NOT NULL,
|
|
212
|
+
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
213
|
+
description TEXT,
|
|
214
|
+
author_id TEXT NOT NULL,
|
|
215
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
216
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
217
|
+
)`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
{{/if}}
|
|
75
221
|
{{#if hasDatabase}}
|
|
76
222
|
{{#if hasAuth}}
|
|
223
|
+
{{#if isPrisma8}}
|
|
224
|
+
async findAll(authorId: string): Promise<Todo[]> {
|
|
225
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
226
|
+
async findAll(authorId: string) {
|
|
227
|
+
{{else}}
|
|
77
228
|
findAll(authorId: string) {
|
|
229
|
+
{{/if}}
|
|
78
230
|
{{#if isPrisma8}}
|
|
79
|
-
return
|
|
231
|
+
return await this.db.prisma.orm.public.Todo.where({ authorId }).all();
|
|
80
232
|
{{else if usePrisma}}
|
|
81
233
|
return this.db.prisma.todo.findMany({ where: { authorId } });
|
|
234
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
235
|
+
const pool = this.db.raw.sql as Pool;
|
|
236
|
+
const result = await pool.query(
|
|
237
|
+
'SELECT id, title, completed, description, author_id AS "authorId" FROM todos WHERE author_id = $1 ORDER BY id',
|
|
238
|
+
[authorId]
|
|
239
|
+
);
|
|
240
|
+
return result.rows;
|
|
82
241
|
{{else if useTypeorm}}
|
|
83
242
|
return this.db.typeorm.getRepository(Todo).find({ where: { authorId } });
|
|
84
243
|
{{else if useDrizzle}}
|
|
@@ -86,16 +245,32 @@ export class TodoService {
|
|
|
86
245
|
{{/if}}
|
|
87
246
|
}
|
|
88
247
|
|
|
248
|
+
{{#if isPrisma8}}
|
|
249
|
+
async create(input: CreateTodoDto, authorId: string): Promise<Todo> {
|
|
250
|
+
{{else}}
|
|
89
251
|
async create(input: CreateTodoDto, authorId: string) {
|
|
252
|
+
{{/if}}
|
|
90
253
|
{{#if isPrisma8}}
|
|
91
|
-
return
|
|
254
|
+
return await this.db.prisma.orm.public.Todo.create({ ...input, authorId });
|
|
92
255
|
{{else if usePrisma}}
|
|
93
256
|
const todo = await this.db.prisma.todo.create({ data: { ...input, authorId } });
|
|
94
257
|
return todo;
|
|
258
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
259
|
+
const pool = this.db.raw.sql as Pool;
|
|
260
|
+
const result = await pool.query(
|
|
261
|
+
'INSERT INTO todos (title, completed, description, author_id) VALUES ($1, $2, $3, $4) RETURNING id, title, completed, description, author_id AS "authorId"',
|
|
262
|
+
[input.title, input.completed ?? false, input.description ?? null, authorId]
|
|
263
|
+
);
|
|
264
|
+
const [todo] = result.rows;
|
|
265
|
+
return todo;
|
|
95
266
|
{{else if useTypeorm}}
|
|
96
267
|
const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create({ ...input, authorId }));
|
|
97
268
|
return todo;
|
|
98
269
|
{{else if useDrizzle}}
|
|
270
|
+
{{#if hasSqlserver}}
|
|
271
|
+
const [todo] = await this.db.drizzle.insert(schema.todos).output().values({ ...input, authorId });
|
|
272
|
+
return todo;
|
|
273
|
+
{{else}}
|
|
99
274
|
{{#if hasMysql}}
|
|
100
275
|
const [inserted] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).$returningId();
|
|
101
276
|
if (!inserted) {
|
|
@@ -107,12 +282,13 @@ export class TodoService {
|
|
|
107
282
|
const [todo] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).returning();
|
|
108
283
|
return todo;
|
|
109
284
|
{{/if}}
|
|
285
|
+
{{/if}}
|
|
110
286
|
{{/if}}
|
|
111
287
|
}
|
|
112
288
|
|
|
113
289
|
async update(id: string, input: UpdateTodoDto, authorId: string) {
|
|
114
290
|
{{#if isPrisma8}}
|
|
115
|
-
const result = await
|
|
291
|
+
const result = await this.db.prisma.orm.public.Todo.where({ authorId, id: Number(id) }).update(input);
|
|
116
292
|
if (!result) {
|
|
117
293
|
throw new NotFoundException("Todo not found");
|
|
118
294
|
}
|
|
@@ -123,6 +299,17 @@ export class TodoService {
|
|
|
123
299
|
throw new NotFoundException("Todo not found");
|
|
124
300
|
}
|
|
125
301
|
return this.db.prisma.todo.update({ data: input, where: { id } });
|
|
302
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
303
|
+
const pool = this.db.raw.sql as Pool;
|
|
304
|
+
const result = await pool.query(
|
|
305
|
+
'UPDATE todos SET title = COALESCE($1, title), completed = COALESCE($2, completed), description = COALESCE($3, description), updated_at = NOW() WHERE id = $4 AND author_id = $5 RETURNING id, title, completed, description, author_id AS "authorId"',
|
|
306
|
+
[input.title ?? null, input.completed ?? null, input.description ?? null, id, authorId]
|
|
307
|
+
);
|
|
308
|
+
const [todo] = result.rows;
|
|
309
|
+
if (!todo) {
|
|
310
|
+
throw new NotFoundException("Todo not found");
|
|
311
|
+
}
|
|
312
|
+
return todo;
|
|
126
313
|
{{else if useTypeorm}}
|
|
127
314
|
const repository = this.db.typeorm.getRepository(Todo);
|
|
128
315
|
const result = await repository.update({ authorId, id }, input);
|
|
@@ -138,6 +325,8 @@ export class TodoService {
|
|
|
138
325
|
{{#if hasMysql}}
|
|
139
326
|
await this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
140
327
|
const [todo] = await this.db.drizzle.select().from(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
328
|
+
{{else if hasSqlserver}}
|
|
329
|
+
const [todo] = await this.db.drizzle.update(schema.todos).set(input).output().where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
141
330
|
{{else}}
|
|
142
331
|
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
332
|
{{/if}}
|
|
@@ -150,7 +339,7 @@ export class TodoService {
|
|
|
150
339
|
|
|
151
340
|
async delete(id: string, authorId: string) {
|
|
152
341
|
{{#if isPrisma8}}
|
|
153
|
-
const count = await
|
|
342
|
+
const count = await this.db.prisma.orm.public.Todo.where({ authorId, id }).delete();
|
|
154
343
|
if (count === 0) {
|
|
155
344
|
throw new NotFoundException("Todo not found");
|
|
156
345
|
}
|
|
@@ -159,6 +348,16 @@ export class TodoService {
|
|
|
159
348
|
if (result.count === 0) {
|
|
160
349
|
throw new NotFoundException("Todo not found");
|
|
161
350
|
}
|
|
351
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
352
|
+
const pool = this.db.raw.sql as Pool;
|
|
353
|
+
const result = await pool.query(
|
|
354
|
+
'DELETE FROM todos WHERE id = $1 AND author_id = $2 RETURNING id',
|
|
355
|
+
[id, authorId]
|
|
356
|
+
);
|
|
357
|
+
const [todo] = result.rows;
|
|
358
|
+
if (!todo) {
|
|
359
|
+
throw new NotFoundException("Todo not found");
|
|
360
|
+
}
|
|
162
361
|
{{else if useTypeorm}}
|
|
163
362
|
const result = await this.db.typeorm.getRepository(Todo).delete({ authorId, id });
|
|
164
363
|
if (result.affected === 0) {
|
|
@@ -171,6 +370,11 @@ export class TodoService {
|
|
|
171
370
|
throw new NotFoundException("Todo not found");
|
|
172
371
|
}
|
|
173
372
|
await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
373
|
+
{{else if hasSqlserver}}
|
|
374
|
+
const [todo] = await this.db.drizzle.delete(schema.todos).output().where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
375
|
+
if (!todo) {
|
|
376
|
+
throw new NotFoundException("Todo not found");
|
|
377
|
+
}
|
|
174
378
|
{{else}}
|
|
175
379
|
const [todo] = await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
|
|
176
380
|
if (!todo) {
|
|
@@ -180,10 +384,14 @@ export class TodoService {
|
|
|
180
384
|
{{/if}}
|
|
181
385
|
return { message: "Todo deleted successfully" };
|
|
182
386
|
}
|
|
387
|
+
{{else}}
|
|
388
|
+
{{#if isPrisma8}}
|
|
389
|
+
async findAll(): Promise<Todo[]> {
|
|
183
390
|
{{else}}
|
|
184
391
|
findAll() {
|
|
392
|
+
{{/if}}
|
|
185
393
|
{{#if isPrisma8}}
|
|
186
|
-
return
|
|
394
|
+
return await this.db.prisma.orm.public.Todo.all();
|
|
187
395
|
{{else if usePrisma}}
|
|
188
396
|
return this.db.prisma.todo.findMany();
|
|
189
397
|
{{else if useTypeorm}}
|
|
@@ -193,9 +401,13 @@ export class TodoService {
|
|
|
193
401
|
{{/if}}
|
|
194
402
|
}
|
|
195
403
|
|
|
404
|
+
{{#if isPrisma8}}
|
|
405
|
+
async create(input: CreateTodoDto): Promise<Todo> {
|
|
406
|
+
{{else}}
|
|
196
407
|
async create(input: CreateTodoDto) {
|
|
408
|
+
{{/if}}
|
|
197
409
|
{{#if isPrisma8}}
|
|
198
|
-
return
|
|
410
|
+
return await this.db.prisma.orm.public.Todo.create(input);
|
|
199
411
|
{{else if usePrisma}}
|
|
200
412
|
const todo = await this.db.prisma.todo.create({ data: input });
|
|
201
413
|
return todo;
|
|
@@ -203,67 +415,135 @@ export class TodoService {
|
|
|
203
415
|
const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create(input));
|
|
204
416
|
return todo;
|
|
205
417
|
{{else if useDrizzle}}
|
|
418
|
+
{{#if hasSqlserver}}
|
|
419
|
+
const [todo] = await this.db.drizzle.insert(schema.todos).output().values(input);
|
|
420
|
+
{{else if hasMysql}}
|
|
421
|
+
const [inserted] = await this.db.drizzle.insert(schema.todos).values(input).$returningId();
|
|
422
|
+
if (!inserted) {
|
|
423
|
+
throw new NotFoundException("Todo not found");
|
|
424
|
+
}
|
|
425
|
+
const [todo] = await this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.id, inserted.id));
|
|
426
|
+
{{else}}
|
|
206
427
|
const [todo] = await this.db.drizzle.insert(schema.todos).values(input).returning();
|
|
428
|
+
{{/if}}
|
|
207
429
|
return todo;
|
|
208
430
|
{{/if}}
|
|
209
431
|
}
|
|
210
432
|
|
|
433
|
+
{{#if isPrisma8}}
|
|
434
|
+
async update(id: string, input: UpdateTodoDto): Promise<Todo> {
|
|
435
|
+
{{else}}
|
|
211
436
|
async update(id: string, input: UpdateTodoDto) {
|
|
437
|
+
{{/if}}
|
|
212
438
|
{{#if isPrisma8}}
|
|
213
|
-
|
|
439
|
+
const todo = await this.db.prisma.orm.public.Todo.where({ id: Number(id) }).update(input);
|
|
440
|
+
if (!todo) {
|
|
441
|
+
throw new NotFoundException("Todo not found");
|
|
442
|
+
}
|
|
443
|
+
return todo;
|
|
214
444
|
{{else if usePrisma}}
|
|
445
|
+
{{#if (and hasMongodb (not isPrisma8))}}
|
|
446
|
+
this.assertMongoId(id);
|
|
447
|
+
const existingTodo = await this.db.prisma.todo.findUnique({ where: { id } });
|
|
448
|
+
if (!existingTodo) {
|
|
449
|
+
throw new NotFoundException("Todo not found");
|
|
450
|
+
}
|
|
451
|
+
{{/if}}
|
|
215
452
|
const result = await this.db.prisma.todo.update({ data: input, where: { id } });
|
|
216
453
|
return result;
|
|
217
454
|
{{else if useTypeorm}}
|
|
218
|
-
const
|
|
219
|
-
|
|
455
|
+
const repository = this.db.typeorm.getRepository(Todo);
|
|
456
|
+
const result = await repository.update(id, input);
|
|
457
|
+
if (result.affected === 0) {
|
|
458
|
+
throw new NotFoundException("Todo not found");
|
|
459
|
+
}
|
|
460
|
+
const todo = await repository.findOneBy({ id });
|
|
461
|
+
if (!todo) {
|
|
462
|
+
throw new NotFoundException("Todo not found");
|
|
463
|
+
}
|
|
464
|
+
return todo;
|
|
220
465
|
{{else if useDrizzle}}
|
|
466
|
+
{{#if hasSqlserver}}
|
|
467
|
+
const [todo] = await this.db.drizzle.update(schema.todos).set(input).output().where(eq(schema.todos.id, Number(id)));
|
|
468
|
+
if (!todo) {
|
|
469
|
+
throw new NotFoundException("Todo not found");
|
|
470
|
+
}
|
|
471
|
+
{{else if hasMysql}}
|
|
472
|
+
await this.db.drizzle.update(schema.todos).set(input).where(eq(schema.todos.id, Number(id)));
|
|
473
|
+
const [todo] = await this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.id, Number(id)));
|
|
474
|
+
if (!todo) {
|
|
475
|
+
throw new NotFoundException("Todo not found");
|
|
476
|
+
}
|
|
477
|
+
{{else}}
|
|
221
478
|
const [todo] = await this.db.drizzle.update(schema.todos).set(input).where(eq(schema.todos.id, Number(id))).returning();
|
|
479
|
+
{{/if}}
|
|
222
480
|
return todo;
|
|
223
481
|
{{/if}}
|
|
224
482
|
}
|
|
225
483
|
|
|
226
484
|
async delete(id: string) {
|
|
227
485
|
{{#if isPrisma8}}
|
|
228
|
-
await
|
|
486
|
+
await this.db.prisma.orm.public.Todo.where({ id: Number(id) }).delete();
|
|
229
487
|
{{else if usePrisma}}
|
|
488
|
+
{{#if (and hasMongodb (not isPrisma8))}}
|
|
489
|
+
this.assertMongoId(id);
|
|
490
|
+
const existingTodo = await this.db.prisma.todo.findUnique({ where: { id } });
|
|
491
|
+
if (!existingTodo) {
|
|
492
|
+
throw new NotFoundException("Todo not found");
|
|
493
|
+
}
|
|
494
|
+
{{/if}}
|
|
230
495
|
await this.db.prisma.todo.delete({ where: { id } });
|
|
231
496
|
{{else if useTypeorm}}
|
|
232
497
|
await this.db.typeorm.getRepository(Todo).delete(id);
|
|
233
498
|
{{else if useDrizzle}}
|
|
499
|
+
{{#if hasSqlserver}}
|
|
500
|
+
const [todo] = await this.db.drizzle.delete(schema.todos).output().where(eq(schema.todos.id, Number(id)));
|
|
501
|
+
if (!todo) {
|
|
502
|
+
throw new NotFoundException("Todo not found");
|
|
503
|
+
}
|
|
504
|
+
{{else}}
|
|
234
505
|
await this.db.drizzle.delete(schema.todos).where(eq(schema.todos.id, Number(id)));
|
|
506
|
+
{{/if}}
|
|
235
507
|
{{/if}}
|
|
236
508
|
return { message: "Todo deleted successfully" };
|
|
237
509
|
}
|
|
238
510
|
{{/if}}
|
|
239
511
|
{{else}}
|
|
240
|
-
findAll() {
|
|
512
|
+
findAll({{#if hasAuth}}authorId: string{{/if}}) {
|
|
513
|
+
{{#if hasAuth}}
|
|
514
|
+
return this.todos.filter((todo) => todo.authorId === authorId);
|
|
515
|
+
{{else}}
|
|
241
516
|
return [...this.todos];
|
|
517
|
+
{{/if}}
|
|
242
518
|
}
|
|
243
519
|
|
|
244
|
-
create(input: CreateTodoDto) {
|
|
245
|
-
const
|
|
520
|
+
create(input: CreateTodoDto{{#if hasAuth}}, authorId: string{{/if}}) {
|
|
521
|
+
const id = String(this.nextId);
|
|
522
|
+
this.nextId += 1;
|
|
523
|
+
const todo = { id, ...input{{#if hasAuth}}, authorId{{/if}} };
|
|
246
524
|
this.todos.push(todo);
|
|
247
525
|
return todo;
|
|
248
526
|
}
|
|
249
527
|
|
|
250
|
-
update(id: string, input: UpdateTodoDto) {
|
|
251
|
-
const todo = this.todos.find((
|
|
528
|
+
update(id: string, input: UpdateTodoDto{{#if hasAuth}}, authorId: string{{/if}}) {
|
|
529
|
+
const todo = this.todos.find((item) => item.id === id{{#if hasAuth}} && item.authorId === authorId{{/if}});
|
|
252
530
|
if (!todo) {
|
|
253
|
-
|
|
531
|
+
throw new NotFoundException("Todo not found");
|
|
254
532
|
}
|
|
255
533
|
Object.assign(todo, input);
|
|
256
534
|
return todo;
|
|
257
535
|
}
|
|
258
536
|
|
|
259
|
-
|
|
260
|
-
const index = this.todos.findIndex((
|
|
261
|
-
if (index
|
|
262
|
-
|
|
537
|
+
delete(id: string{{#if hasAuth}}, authorId: string{{/if}}) {
|
|
538
|
+
const index = this.todos.findIndex((item) => item.id === id{{#if hasAuth}} && item.authorId === authorId{{/if}});
|
|
539
|
+
if (index === -1) {
|
|
540
|
+
throw new NotFoundException("Todo not found");
|
|
263
541
|
}
|
|
542
|
+
this.todos.splice(index, 1);
|
|
264
543
|
return { message: "Todo deleted successfully" };
|
|
265
544
|
}
|
|
266
545
|
{{/if}}
|
|
267
546
|
}
|
|
268
547
|
|
|
269
548
|
{{/if}}
|
|
549
|
+
{{/if}}
|
|
@@ -51,7 +51,7 @@ async function bootstrap() {
|
|
|
51
51
|
{{/if}}
|
|
52
52
|
{{#if (includes apis "trpc")}}
|
|
53
53
|
trpc: {
|
|
54
|
-
routerFilePath: "./
|
|
54
|
+
routerFilePath: "./{{trpcGeneratePath}}/server.ts",
|
|
55
55
|
pathPrefix: "/trpc",
|
|
56
56
|
},
|
|
57
57
|
{{/if}}
|
|
@@ -79,7 +79,7 @@ async function bootstrap() {
|
|
|
79
79
|
{{/if}}
|
|
80
80
|
{{#if (includes apis "trpc")}}
|
|
81
81
|
trpc: {
|
|
82
|
-
routerFilePath: "./
|
|
82
|
+
routerFilePath: "./{{trpcGeneratePath}}/server.ts",
|
|
83
83
|
pathPrefix: "/trpc",
|
|
84
84
|
},
|
|
85
85
|
{{/if}}
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
7
|
+
{{#if setupInstructions}}
|
|
8
|
+
{{{setupInstructions}}}
|
|
9
|
+
{{else}}
|
|
7
10
|
```bash
|
|
8
11
|
# Install dependencies
|
|
9
12
|
{{#if isPnpm}}pnpm install{{else if isYarn}}yarn install{{else if isBun}}bun install{{else}}npm install{{/if}}
|
|
10
13
|
```
|
|
14
|
+
{{/if}}
|
|
11
15
|
|
|
12
16
|
## Running the App
|
|
13
17
|
|
|
@@ -35,20 +39,11 @@
|
|
|
35
39
|
{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
|
|
36
40
|
## Auth Setup
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
{{#if useDrizzle}}Then run Drizzle migrations:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
{{#if isPnpm}}pnpm drizzle-kit generate && pnpm drizzle-kit migrate{{else if isYarn}}yarn drizzle-kit generate && yarn drizzle-kit migrate{{else}}npx drizzle-kit generate && npx drizzle-kit migrate{{/if}}
|
|
48
|
-
```
|
|
49
|
-
|
|
42
|
+
{{#if (eq auth "better-auth:stateless")}}
|
|
43
|
+
Configure the Better Auth secret, URL and external OIDC provider settings. Register the callback URL with your provider. Stateless auth requires no authentication schema generation or database migration.
|
|
44
|
+
{{else}}
|
|
45
|
+
Follow the ordered steps in Setup. Run the generated `auth:generate` script in `apps/api-gateway` before applying the database schema and regenerating Prisma Client in `packages/db`. The script uses the installed CLI version and preserves application-owned models.
|
|
50
46
|
{{/if}}
|
|
51
|
-
See https://better-auth.com/docs/adapters/prisma for more details.
|
|
52
47
|
|
|
53
48
|
### Usage
|
|
54
49
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
CORS_ORIGIN=http://localhost:
|
|
1
|
+
CORS_ORIGIN=http://localhost:3000
|
|
2
2
|
PORT=3000
|
|
3
3
|
{{#if hasGraphql}}
|
|
4
4
|
GRAPHQL_PLAYGROUND=true
|
|
@@ -25,7 +25,11 @@ SQLSERVER_TRUST_SERVER_CERTIFICATE=true
|
|
|
25
25
|
{{#if hasMongodb}}
|
|
26
26
|
MONGODB_URL=mongodb://localhost:27017/{{projectNameKebab}}_db
|
|
27
27
|
{{/if}}
|
|
28
|
-
{{#if (eq auth "passport")}}
|
|
28
|
+
{{#if (and (eq auth "passport") (not hasDatabase))}}
|
|
29
|
+
OIDC_JWKS_URI=https://identity.example.com/.well-known/jwks.json
|
|
30
|
+
OIDC_ISSUER=https://identity.example.com/
|
|
31
|
+
OIDC_AUDIENCE=generated-resource-server
|
|
32
|
+
{{else if (eq auth "passport")}}
|
|
29
33
|
JWT_ACCESS_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
|
|
30
34
|
{{#if hasDatabase}}
|
|
31
35
|
JWT_REFRESH_SECRET=change-me-to-a-different-random-secret-at-least-32-characters
|
|
@@ -12,5 +12,11 @@ allowBuilds:
|
|
|
12
12
|
{{#if hasSqlite}}
|
|
13
13
|
better-sqlite3: true
|
|
14
14
|
{{/if}}
|
|
15
|
+
{{#if hasTrpc}}
|
|
16
|
+
peerDependencyRules:
|
|
17
|
+
allowedVersions:
|
|
18
|
+
"nestjs-trpc>@nestjs/common": "^12.0.0"
|
|
19
|
+
"nestjs-trpc>@nestjs/core": "^12.0.0"
|
|
20
|
+
{{/if}}
|
|
15
21
|
catalog:
|
|
16
22
|
dotenv: ^17.2.2
|
|
@@ -71,8 +71,8 @@ async function bootstrap() {
|
|
|
71
71
|
{{/if}}
|
|
72
72
|
{{#if (includes apis "trpc")}}
|
|
73
73
|
trpc: {
|
|
74
|
-
routerFilePath: "./src/trpc/router.ts",
|
|
75
74
|
pathPrefix: "/trpc",
|
|
75
|
+
routerFilePath: "./{{trpcGeneratePath}}/server.ts",
|
|
76
76
|
},
|
|
77
77
|
{{/if}}
|
|
78
78
|
zod: {
|
|
@@ -99,8 +99,8 @@ async function bootstrap() {
|
|
|
99
99
|
{{/if}}
|
|
100
100
|
{{#if (includes apis "trpc")}}
|
|
101
101
|
trpc: {
|
|
102
|
-
routerFilePath: "./src/trpc/router.ts",
|
|
103
102
|
pathPrefix: "/trpc",
|
|
103
|
+
routerFilePath: "./{{trpcGeneratePath}}/server.ts",
|
|
104
104
|
},
|
|
105
105
|
{{/if}}
|
|
106
106
|
zod: {
|
|
@@ -43,7 +43,7 @@ async function bootstrap() {
|
|
|
43
43
|
{{/if}}
|
|
44
44
|
{{#if (includes apis "trpc")}}
|
|
45
45
|
trpc: {
|
|
46
|
-
routerFilePath: "./
|
|
46
|
+
routerFilePath: "./{{trpcGeneratePath}}/server.ts",
|
|
47
47
|
pathPrefix: "/trpc",
|
|
48
48
|
},
|
|
49
49
|
{{/if}}
|
|
@@ -71,7 +71,7 @@ async function bootstrap() {
|
|
|
71
71
|
{{/if}}
|
|
72
72
|
{{#if (includes apis "trpc")}}
|
|
73
73
|
trpc: {
|
|
74
|
-
routerFilePath: "./
|
|
74
|
+
routerFilePath: "./{{trpcGeneratePath}}/server.ts",
|
|
75
75
|
pathPrefix: "/trpc",
|
|
76
76
|
},
|
|
77
77
|
{{/if}}
|