@nest-arch/tui 0.1.48 → 0.2.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.
- package/dist/cli.js +1 -1
- package/dist/constants-BbJ71cex.js +1 -0
- package/dist/index.js +61 -61
- package/dist/templates/base/.gitignore.hbs +3 -0
- package/dist/templates/base/README.md.hbs +119 -6
- package/dist/templates/base/pnpm-workspace.yaml.hbs +3 -0
- package/dist/templates/base/src/app.controller.ts.hbs +10 -0
- package/dist/templates/base/src/app.module.ts.hbs +33 -4
- package/dist/templates/base/src/app.router.spec.ts.hbs +58 -0
- package/dist/templates/base/src/app.router.ts.hbs +26 -0
- package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +3 -3
- package/dist/templates/base/src/libs/middlewares/auth.trpc.middleware.ts.hbs +42 -0
- package/dist/templates/base/src/libs/trpc/context.ts.hbs +13 -0
- package/dist/templates/base/src/libs/trpc/error.handler.ts.hbs +10 -0
- package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +7 -0
- package/dist/templates/base/src/libs/trpc/protected.ts.hbs +12 -0
- package/dist/templates/base/src/libs/trpc/scripts/fix-trpc-generated.mjs.hbs +21 -0
- package/dist/templates/base/src/libs/trpc/scripts/watch-trpc.mjs.hbs +91 -0
- package/dist/templates/base/src/main.ts.hbs +74 -1
- package/dist/templates/extras/swagger/create-docs.ts.hbs +232 -0
- package/dist/templates/monorepo/apps/api-gateway/src/app.controller.ts.hbs +10 -0
- package/dist/templates/monorepo/apps/api-gateway/src/app.module.ts.hbs +26 -1
- package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +73 -0
- package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +3 -0
- package/dist/templates/single-project/architecture/nest-api/pnpm-workspace.yaml.hbs +3 -0
- package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +73 -0
- package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +73 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +74 -0
- package/package.json +3 -3
- package/dist/constants-D_Ow0fiZ.js +0 -1
|
@@ -36,22 +36,54 @@ npm run start{{/if}}
|
|
|
36
36
|
{{#if isPnpm}}pnpm test:cov{{else if isYarn}}yarn test:cov{{else if isBun}}bun run test:cov{{else}}npm run test:cov{{/if}}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
{{#if
|
|
40
|
-
##
|
|
39
|
+
{{#if usePrisma}}
|
|
40
|
+
## Database Setup
|
|
41
41
|
|
|
42
|
-
This project uses **
|
|
42
|
+
This project uses **Prisma** as the ORM. Available commands:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
{{#if isPnpm}}pnpm
|
|
45
|
+
{{#if isPnpm}}pnpm prisma:generate{{else if isYarn}}yarn prisma:generate{{else if isBun}}bun run prisma:generate{{else}}npm run prisma:generate{{/if}}
|
|
46
|
+
{{#if isPnpm}}pnpm prisma:push{{else if isYarn}}yarn prisma:push{{else if isBun}}bun run prisma:push{{else}}npm run prisma:push{{/if}}
|
|
47
|
+
{{#if isPnpm}}pnpm prisma:studio{{else if isYarn}}yarn prisma:studio{{else if isBun}}bun run prisma:studio{{else}}npm run prisma:studio{{/if}}
|
|
48
|
+
{{#if isPnpm}}pnpm prisma:migrate{{else if isYarn}}yarn prisma:migrate{{else if isBun}}bun run prisma:migrate{{else}}npm run prisma:migrate{{/if}}
|
|
49
|
+
{{#if isPnpm}}pnpm prisma:migrate:deploy{{else if isYarn}}yarn prisma:migrate:deploy{{else if isBun}}bun run prisma:migrate:deploy{{else}}npm run prisma:migrate:deploy{{/if}}
|
|
46
50
|
```
|
|
51
|
+
{{/if}}
|
|
52
|
+
|
|
53
|
+
{{#if useDrizzle}}
|
|
54
|
+
## Database Setup
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
This project uses **Drizzle** as the ORM. Available commands:
|
|
49
57
|
|
|
50
58
|
```bash
|
|
51
|
-
{{#if isPnpm}}pnpm
|
|
59
|
+
{{#if isPnpm}}pnpm db:generate{{else if isYarn}}yarn db:generate{{else if isBun}}bun run db:generate{{else}}npm run db:generate{{/if}}
|
|
60
|
+
{{#if isPnpm}}pnpm db:migrate{{else if isYarn}}yarn db:migrate{{else if isBun}}bun run db:migrate{{else}}npm run db:migrate{{/if}}
|
|
61
|
+
{{#if isPnpm}}pnpm db:studio{{else if isYarn}}yarn db:studio{{else if isBun}}bun run db:studio{{else}}npm run db:studio{{/if}}
|
|
62
|
+
{{#if isPnpm}}pnpm db:push{{else if isYarn}}yarn db:push{{else if isBun}}bun run db:push{{else}}npm run db:push{{/if}}
|
|
52
63
|
```
|
|
64
|
+
{{/if}}
|
|
65
|
+
|
|
66
|
+
{{#if useTypeorm}}
|
|
67
|
+
## Database Setup
|
|
53
68
|
|
|
69
|
+
This project uses **TypeORM** as the ORM. Available commands:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
{{#if isPnpm}}pnpm typeorm:run{{else if isYarn}}yarn typeorm:run{{else if isBun}}bun run typeorm:run{{else}}npm run typeorm:run{{/if}}
|
|
73
|
+
{{#if isPnpm}}pnpm typeorm:revert{{else if isYarn}}yarn typeorm:revert{{else if isBun}}bun run typeorm:revert{{else}}npm run typeorm:revert{{/if}}
|
|
74
|
+
{{#if isPnpm}}pnpm typeorm:generate{{else if isYarn}}yarn typeorm:generate{{else if isBun}}bun run typeorm:generate{{else}}npm run typeorm:generate{{/if}}
|
|
75
|
+
{{#if isPnpm}}pnpm typeorm:create{{else if isYarn}}yarn typeorm:create{{else if isBun}}bun run typeorm:create{{else}}npm run typeorm:create{{/if}}
|
|
76
|
+
```
|
|
54
77
|
{{/if}}
|
|
78
|
+
|
|
79
|
+
{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
|
|
80
|
+
## Auth Setup
|
|
81
|
+
|
|
82
|
+
This project uses **Better Auth**. Generate the database schema by running:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
{{#if isPnpm}}pnpm dlx auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y{{else if isYarn}}yarn dlx auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y{{else if isBun}}bun x auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y{{else}}npx auth@latest generate --config src/auth/auth.ts --output src/database/orm/prisma/schema.prisma -y{{/if}}
|
|
86
|
+
```
|
|
55
87
|
See https://better-auth.com/docs/adapters/prisma for more details.
|
|
56
88
|
|
|
57
89
|
### Usage
|
|
@@ -72,3 +104,84 @@ export class AppController {
|
|
|
72
104
|
}
|
|
73
105
|
```
|
|
74
106
|
{{/if}}
|
|
107
|
+
{{#if hasTrpc}}
|
|
108
|
+
## tRPC Setup
|
|
109
|
+
|
|
110
|
+
This project uses **nestjs-trpc** with decorator-based routing.
|
|
111
|
+
|
|
112
|
+
### Generating tRPC Types
|
|
113
|
+
|
|
114
|
+
After creating or modifying routers, generate the tRPC types:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
{{#if isPnpm}}pnpm trpc:generate{{else if isYarn}}yarn trpc:generate{{else if isBun}}bun run trpc:generate{{else}}npm run trpc:generate{{/if}}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For watch mode (auto-regenerates on changes):
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
{{#if isPnpm}}pnpm trpc:watch{{else if isYarn}}yarn trpc:watch{{else if isBun}}bun run trpc:watch{{else}}npm run trpc:watch{{/if}}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
{{#if hasTrpcAndAuth}}
|
|
127
|
+
### Router Example (with Auth)
|
|
128
|
+
|
|
129
|
+
Routers are defined using the `@Router` decorator from `nestjs-trpc`. **Router names must start with a lowercase letter.**
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { Router, Query, UseMiddlewares, Ctx } from "nestjs-trpc";
|
|
133
|
+
import z from "zod";
|
|
134
|
+
|
|
135
|
+
import { AppService } from "./app.service.js";
|
|
136
|
+
import type { UserSession } from "./auth/auth.js";
|
|
137
|
+
import { AuthTRPCMiddleware } from "./libs/middlewares/auth.trpc.middleware.js";
|
|
138
|
+
import type { InnerContext } from "./libs/trpc/inner.context.js";
|
|
139
|
+
|
|
140
|
+
@Router({ alias: "appRouter" })
|
|
141
|
+
export class AppRouterClass {
|
|
142
|
+
constructor(private readonly appService: AppService) {}
|
|
143
|
+
|
|
144
|
+
@Query()
|
|
145
|
+
@UseMiddlewares(AuthTRPCMiddleware)
|
|
146
|
+
testAuth(@Ctx() ctx: InnerContext): UserSession {
|
|
147
|
+
return ctx.session!;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@Query({ output: z.string() })
|
|
151
|
+
getHello(): string {
|
|
152
|
+
return this.appService.getHello();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
{{else}}
|
|
157
|
+
### Router Example
|
|
158
|
+
|
|
159
|
+
Routers are defined using the `@Router` decorator from `nestjs-trpc`. **Router names must start with a lowercase letter.**
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { Router, Query } from "nestjs-trpc";
|
|
163
|
+
import z from "zod";
|
|
164
|
+
|
|
165
|
+
@Router({ alias: "appRouter" })
|
|
166
|
+
export class AppRouterClass {
|
|
167
|
+
constructor(private readonly appService: AppService) {}
|
|
168
|
+
|
|
169
|
+
@Query({ output: z.string() })
|
|
170
|
+
getHello(): string {
|
|
171
|
+
return this.appService.getHello();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
{{/if}}
|
|
176
|
+
|
|
177
|
+
Import the router in your module:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { AppRouterClass as AppRouter } from "./app.router.js";
|
|
181
|
+
|
|
182
|
+
@Module({
|
|
183
|
+
providers: [AppRouter],
|
|
184
|
+
})
|
|
185
|
+
export class AppModule {}
|
|
186
|
+
```
|
|
187
|
+
{{/if}}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { Controller, Get } from "@nestjs/common";
|
|
2
|
+
{{#if swagger}}
|
|
3
|
+
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
|
4
|
+
{{/if}}
|
|
2
5
|
{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
|
|
3
6
|
import { AllowAnonymous } from "@thallesp/nestjs-better-auth";
|
|
4
7
|
{{/if}}
|
|
5
8
|
import { AppService } from "./app.service.js";
|
|
6
9
|
|
|
10
|
+
{{#if swagger}}
|
|
11
|
+
@ApiTags("App")
|
|
12
|
+
{{/if}}
|
|
7
13
|
@Controller()
|
|
8
14
|
export class AppController {
|
|
9
15
|
constructor(private readonly appService: AppService) {}
|
|
@@ -11,6 +17,10 @@ export class AppController {
|
|
|
11
17
|
{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
|
|
12
18
|
@AllowAnonymous()
|
|
13
19
|
{{/if}}
|
|
20
|
+
{{#if swagger}}
|
|
21
|
+
@ApiOperation({ summary: "/api" })
|
|
22
|
+
@ApiResponse({ status: 200, description: "Returns a hello world message" })
|
|
23
|
+
{{/if}}
|
|
14
24
|
@Get()
|
|
15
25
|
getHello(): string {
|
|
16
26
|
return this.appService.getHello();
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import { Module } from "@nestjs/common";
|
|
2
2
|
{{#if (or hasDatabase hasOrm)}}
|
|
3
3
|
import { env } from "./config/env.js";
|
|
4
|
-
{{/if}}
|
|
5
4
|
import { DbModule } from "./database/db.module.js";
|
|
6
|
-
{{
|
|
5
|
+
{{/if}}
|
|
6
|
+
{{#if shouldImportAuthModule}}
|
|
7
7
|
import { AuthModule } from "./auth/auth.module.js";
|
|
8
8
|
{{/if}}
|
|
9
|
+
{{#if hasTrpc}}
|
|
10
|
+
import { TRPCModule } from "nestjs-trpc";
|
|
11
|
+
import { AppRouterClass as AppRouter } from "./app.router.js";
|
|
12
|
+
import { AppContext } from "./libs/trpc/context.js";
|
|
13
|
+
import { AppErrorHandler } from "./libs/trpc/error.handler.js";
|
|
14
|
+
{{#if hasTrpcAndAuth}}
|
|
15
|
+
import { AuthTRPCMiddleware } from "./libs/middlewares/auth.trpc.middleware.js";
|
|
16
|
+
{{/if}}
|
|
17
|
+
{{/if}}
|
|
9
18
|
|
|
19
|
+
{{#if hasRest}}
|
|
10
20
|
import { AppController } from "./app.controller.js";
|
|
21
|
+
{{/if}}
|
|
11
22
|
import { AppService } from "./app.service.js";
|
|
12
23
|
|
|
13
24
|
@Module({
|
|
25
|
+
{{#if hasRest}}
|
|
14
26
|
controllers: [AppController],
|
|
27
|
+
{{/if}}
|
|
15
28
|
imports: [
|
|
16
29
|
{{#if (or hasDatabase hasOrm)}}
|
|
17
30
|
DbModule.forRoot({
|
|
@@ -21,10 +34,26 @@ import { AppService } from "./app.service.js";
|
|
|
21
34
|
{{#if hasMongodb}}mongodbUrl: env.MONGODB_URL,{{/if}}
|
|
22
35
|
}),
|
|
23
36
|
{{/if}}
|
|
24
|
-
{{#if
|
|
37
|
+
{{#if shouldImportAuthModule}}
|
|
25
38
|
AuthModule,
|
|
26
39
|
{{/if}}
|
|
40
|
+
{{#if hasTrpc}}
|
|
41
|
+
TRPCModule.forRoot({
|
|
42
|
+
context: AppContext,
|
|
43
|
+
onError: AppErrorHandler,
|
|
44
|
+
}),
|
|
45
|
+
{{/if}}
|
|
46
|
+
],
|
|
47
|
+
providers: [
|
|
48
|
+
AppService,
|
|
49
|
+
{{#if hasTrpc}}
|
|
50
|
+
{{#if hasTrpcAndAuth}}
|
|
51
|
+
AuthTRPCMiddleware,
|
|
52
|
+
{{/if}}
|
|
53
|
+
AppContext,
|
|
54
|
+
AppErrorHandler,
|
|
55
|
+
AppRouter,
|
|
56
|
+
{{/if}}
|
|
27
57
|
],
|
|
28
|
-
providers: [AppService],
|
|
29
58
|
})
|
|
30
59
|
export class AppModule {}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Test, TestingModule } from "@nestjs/testing";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { AppRouterClass } from "./app.router.js";
|
|
5
|
+
import { AppService } from "./app.service.js";
|
|
6
|
+
{{#if hasTrpcAndAuth}}
|
|
7
|
+
import { AuthTRPCMiddleware } from "./libs/middlewares/auth.trpc.middleware.js";
|
|
8
|
+
import type { InnerContext } from "./libs/trpc/inner.context.js";
|
|
9
|
+
{{/if}}
|
|
10
|
+
|
|
11
|
+
describe(AppRouterClass, () => {
|
|
12
|
+
let router: AppRouterClass;
|
|
13
|
+
|
|
14
|
+
const mockAppService = {
|
|
15
|
+
getHello: vi.fn<() => string>().mockReturnValue("Hello World!"),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
20
|
+
providers: [
|
|
21
|
+
AppRouterClass,
|
|
22
|
+
{ provide: AppService, useValue: mockAppService },
|
|
23
|
+
{{#if hasTrpcAndAuth}}
|
|
24
|
+
{ provide: AuthTRPCMiddleware, useValue: {} },
|
|
25
|
+
{{/if}}
|
|
26
|
+
],
|
|
27
|
+
}).compile();
|
|
28
|
+
|
|
29
|
+
router = module.get<AppRouterClass>(AppRouterClass);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should be defined", () => {
|
|
33
|
+
expect(router).toBeDefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("getHello", () => {
|
|
37
|
+
it("should return the hello message from AppService", () => {
|
|
38
|
+
const result = router.getHello();
|
|
39
|
+
expect(result).toBe("Hello World!");
|
|
40
|
+
expect(mockAppService.getHello).toHaveBeenCalledWith();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
{{#if hasTrpcAndAuth}}
|
|
45
|
+
describe("testAuth", () => {
|
|
46
|
+
it("should return the session from context", () => {
|
|
47
|
+
const mockCtx = {
|
|
48
|
+
session: {
|
|
49
|
+
session: { expiresAt: new Date() },
|
|
50
|
+
user: { email: "test@example.com", id: "1", name: "Test User" },
|
|
51
|
+
},
|
|
52
|
+
} as InnerContext;
|
|
53
|
+
const result = router.testAuth(mockCtx);
|
|
54
|
+
expect(result).toStrictEqual(mockCtx.session);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
{{/if}}
|
|
58
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Router, Query, UseMiddlewares, Ctx } from "nestjs-trpc";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
{{#if hasTrpcAndAuth}}
|
|
4
|
+
import type { UserSession } from "./auth/auth.js";
|
|
5
|
+
import { AuthTRPCMiddleware } from "./libs/middlewares/auth.trpc.middleware.js";
|
|
6
|
+
import type { InnerContext } from "./libs/trpc/inner.context.js";
|
|
7
|
+
{{/if}}
|
|
8
|
+
import { AppService } from "./app.service.js";
|
|
9
|
+
|
|
10
|
+
@Router({ alias: "appRouter" })
|
|
11
|
+
export class AppRouterClass {
|
|
12
|
+
constructor(private readonly appService: AppService) {}
|
|
13
|
+
|
|
14
|
+
{{#if hasTrpcAndAuth}}
|
|
15
|
+
@Query()
|
|
16
|
+
@UseMiddlewares(AuthTRPCMiddleware)
|
|
17
|
+
testAuth(@Ctx() ctx: InnerContext): UserSession {
|
|
18
|
+
return ctx.session!;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
{{/if}}
|
|
22
|
+
@Query({ output: z.string() })
|
|
23
|
+
getHello(): string {
|
|
24
|
+
return this.appService.getHello();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{{#if (or hasPostgres hasMysql hasSqlite)}}
|
|
2
2
|
export interface ISqlAdapter<TClient = unknown> {
|
|
3
|
-
query<T = unknown>(sql: string, params?: unknown[])
|
|
4
|
-
execute(sql: string, params?: unknown[])
|
|
5
|
-
getClient()
|
|
3
|
+
query: <T = unknown>(sql: string, params?: unknown[]) => Promise<T[]>;
|
|
4
|
+
execute: (sql: string, params?: unknown[]) => Promise<unknown>;
|
|
5
|
+
getClient: () => TClient;
|
|
6
6
|
}
|
|
7
7
|
{{/if}}
|
|
8
8
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { AuthService } from "@thallesp/nestjs-better-auth";
|
|
3
|
+
import { TRPCError } from "@trpc/server";
|
|
4
|
+
import {
|
|
5
|
+
MiddlewareOptions,
|
|
6
|
+
MiddlewareResponse,
|
|
7
|
+
TRPCMiddleware,
|
|
8
|
+
} from "nestjs-trpc";
|
|
9
|
+
import type { ContextOptions } from "nestjs-trpc";
|
|
10
|
+
|
|
11
|
+
import { createAuth } from "../../auth/auth.js";
|
|
12
|
+
|
|
13
|
+
type AppAuth = ReturnType<typeof createAuth>;
|
|
14
|
+
|
|
15
|
+
@Injectable()
|
|
16
|
+
export class AuthTRPCMiddleware implements TRPCMiddleware {
|
|
17
|
+
constructor(private readonly authService: AuthService<AppAuth>) {}
|
|
18
|
+
|
|
19
|
+
async use(
|
|
20
|
+
opts: MiddlewareOptions<ContextOptions>
|
|
21
|
+
): Promise<MiddlewareResponse> {
|
|
22
|
+
const { ctx, next } = opts;
|
|
23
|
+
|
|
24
|
+
const session = await this.authService.api.getSession({
|
|
25
|
+
headers: ctx.req.headers,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (session === null) {
|
|
29
|
+
throw new TRPCError({
|
|
30
|
+
code: "UNAUTHORIZED",
|
|
31
|
+
message: "No session found.",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return next({
|
|
36
|
+
ctx: {
|
|
37
|
+
...ctx,
|
|
38
|
+
session,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// oxlint-disable require-await
|
|
2
|
+
import { Injectable } from "@nestjs/common";
|
|
3
|
+
import { ContextOptions, TRPCContext } from "nestjs-trpc";
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class AppContext implements TRPCContext {
|
|
7
|
+
async create(opts: ContextOptions): Promise<Record<string, unknown>> {
|
|
8
|
+
return {
|
|
9
|
+
req: opts.req,
|
|
10
|
+
res: opts.res,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
2
|
+
import { OnErrorOptions, TRPCErrorHandler } from "nestjs-trpc";
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class AppErrorHandler implements TRPCErrorHandler {
|
|
6
|
+
private logger = new Logger("TRPC Router");
|
|
7
|
+
onError(opts: OnErrorOptions): void {
|
|
8
|
+
this.logger.error(`[${opts.type}] ${opts.path}: ${opts.error.message}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// This script is needed because nestjs-trpc doesn't have support for NestJS 12 yet.
|
|
2
|
+
// Awaiting PR approval to support the new ESM support.
|
|
3
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const generatedFile = path.join(
|
|
7
|
+
import.meta.dirname,
|
|
8
|
+
"..",
|
|
9
|
+
"@generated",
|
|
10
|
+
"server.ts"
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
let content = readFileSync(generatedFile, "utf-8");
|
|
14
|
+
|
|
15
|
+
content = content.replaceAll(
|
|
16
|
+
/(?<ext>\.router)(?<quote>")/gu,
|
|
17
|
+
"$<ext>.js$<quote>"
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
writeFileSync(generatedFile, content, "utf-8");
|
|
21
|
+
console.log("Fixed trpc generated file:", generatedFile);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// This script is needed because nestjs-trpc doesn't have support for NestJS 12 yet.
|
|
2
|
+
// Awaiting PR approval to support the new ESM support.
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { watchFile, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
const generatedFile = path.join(
|
|
8
|
+
import.meta.dirname,
|
|
9
|
+
"..",
|
|
10
|
+
"@generated",
|
|
11
|
+
"server.ts"
|
|
12
|
+
);
|
|
13
|
+
const REGEX_REPLACE = /(?<ext>\.router)(?<quote>")/gu;
|
|
14
|
+
|
|
15
|
+
function fixGeneratedFile() {
|
|
16
|
+
try {
|
|
17
|
+
let content = readFileSync(generatedFile, "utf-8");
|
|
18
|
+
const original = content;
|
|
19
|
+
content = content.replaceAll(REGEX_REPLACE, "$<ext>.js$<quote>");
|
|
20
|
+
if (content !== original) {
|
|
21
|
+
writeFileSync(generatedFile, content, "utf-8");
|
|
22
|
+
console.log("Fixed trpc generated file:", generatedFile);
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// file might not exist yet
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Run generate + fix upfront
|
|
30
|
+
const projectRoot = path.join(import.meta.dirname, "..", "..", "..", "..");
|
|
31
|
+
const SHELL = process.platform === "win32" ? "powershell.exe" : true;
|
|
32
|
+
{{#if isMonorepo}}
|
|
33
|
+
const genCmd =
|
|
34
|
+
"pnpm exec nestjs-trpc generate -e src/app.module.ts -o ../../packages/api/src/trpc/@generated";
|
|
35
|
+
{{else}}
|
|
36
|
+
const genCmd =
|
|
37
|
+
"pnpm exec nestjs-trpc generate -e src/app.module.ts -o src/libs/trpc/@generated";
|
|
38
|
+
{{/if}}
|
|
39
|
+
|
|
40
|
+
const spawnOpts = {
|
|
41
|
+
cwd: projectRoot,
|
|
42
|
+
shell: SHELL,
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const gen = spawn(genCmd, [], spawnOpts);
|
|
47
|
+
|
|
48
|
+
gen.on("close", (code) => {
|
|
49
|
+
if (code !== 0) {
|
|
50
|
+
process.exit(code);
|
|
51
|
+
}
|
|
52
|
+
fixGeneratedFile();
|
|
53
|
+
|
|
54
|
+
// Watch generated file for changes and re-apply fix
|
|
55
|
+
watchFile(generatedFile, { interval: 500 }, () => {
|
|
56
|
+
fixGeneratedFile();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Start watch mode
|
|
60
|
+
{{#if isMonorepo}}
|
|
61
|
+
const watch = spawn(
|
|
62
|
+
"pnpm",
|
|
63
|
+
[
|
|
64
|
+
"exec",
|
|
65
|
+
"nestjs-trpc",
|
|
66
|
+
"watch",
|
|
67
|
+
"-e",
|
|
68
|
+
"src/app.module.ts",
|
|
69
|
+
"-o",
|
|
70
|
+
" ../../packages/api/src/trpc/@generated",
|
|
71
|
+
],
|
|
72
|
+
spawnOpts
|
|
73
|
+
);
|
|
74
|
+
{{else}}
|
|
75
|
+
const watch = spawn(
|
|
76
|
+
"pnpm",
|
|
77
|
+
[
|
|
78
|
+
"exec",
|
|
79
|
+
"nestjs-trpc",
|
|
80
|
+
"watch",
|
|
81
|
+
"-e",
|
|
82
|
+
"src/app.module.ts",
|
|
83
|
+
"-o",
|
|
84
|
+
"src/libs/trpc/@generated",
|
|
85
|
+
],
|
|
86
|
+
spawnOpts
|
|
87
|
+
);
|
|
88
|
+
{{/if}}
|
|
89
|
+
|
|
90
|
+
watch.on("close", (exitCode) => process.exit(exitCode ?? 0));
|
|
91
|
+
});
|
|
@@ -2,16 +2,89 @@ import { NestFactory } from "@nestjs/core";
|
|
|
2
2
|
{{#if (eq httpProvider "fastify")}}
|
|
3
3
|
import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify";
|
|
4
4
|
{{/if}}
|
|
5
|
+
import { Logger } from "@nestjs/common";
|
|
5
6
|
|
|
6
7
|
import { AppModule } from "./app.module.js";
|
|
7
8
|
import { env } from "./config/env.js";
|
|
9
|
+
{{#if swagger}}
|
|
10
|
+
import { createDocs } from "./docs/create-docs.js";
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if (and swagger useSwaggerScalar)}}
|
|
13
|
+
import { apiReference } from "@scalar/nestjs-api-reference";
|
|
14
|
+
{{/if}}
|
|
15
|
+
{{#if (and swagger (eq useSwaggerScalar false))}}
|
|
16
|
+
import { SwaggerModule } from "@nestjs/swagger";
|
|
17
|
+
{{/if}}
|
|
18
|
+
{{#if (and swagger (or (eq auth "better-auth") (eq auth "better-auth:stateless") hasBetterAuthWithoutRest))}}
|
|
19
|
+
import { auth } from "./auth/auth.js";
|
|
20
|
+
{{/if}}
|
|
8
21
|
|
|
9
22
|
async function bootstrap() {
|
|
10
|
-
const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter(){{else}}(AppModule{{/if}}{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}, { bodyParser: false }{{/if}});
|
|
23
|
+
const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter(){{else}}(AppModule{{/if}}{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless") hasBetterAuthWithoutRest)}}, { bodyParser: false }{{/if}});
|
|
11
24
|
app.enableCors({
|
|
12
25
|
origin: env.CORS_ORIGIN,
|
|
13
26
|
});
|
|
14
27
|
app.setGlobalPrefix("api");
|
|
28
|
+
{{#if swagger}}
|
|
29
|
+
const docsPath = "/docs";
|
|
30
|
+
{{#if useSwaggerScalar}}
|
|
31
|
+
app.use(
|
|
32
|
+
docsPath,
|
|
33
|
+
apiReference({
|
|
34
|
+
content: await createDocs(app, {
|
|
35
|
+
description: "{{description}}",
|
|
36
|
+
title: "{{projectNamePascalCase}} API",
|
|
37
|
+
integrations: {
|
|
38
|
+
{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless") hasBetterAuthWithoutRest)}}
|
|
39
|
+
betterAuth: {
|
|
40
|
+
auth,
|
|
41
|
+
pathPrefix: "/api/auth",
|
|
42
|
+
},
|
|
43
|
+
{{/if}}
|
|
44
|
+
{{#if (includes apis "trpc")}}
|
|
45
|
+
trpc: {
|
|
46
|
+
routerFilePath: "./src/libs/trpc/@generated/server.ts",
|
|
47
|
+
pathPrefix: "/trpc",
|
|
48
|
+
},
|
|
49
|
+
{{/if}}
|
|
50
|
+
zod: {
|
|
51
|
+
withFastify: {{#if (eq httpProvider "fastify")}}true{{else}}false{{/if}},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
version: "1.0.0",
|
|
55
|
+
}),
|
|
56
|
+
pageTitle: "{{projectNamePascalCase}} API Docs",
|
|
57
|
+
theme: "default",
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
{{else}}
|
|
61
|
+
const document = await createDocs(app, {
|
|
62
|
+
title: "{{projectNamePascalCase}} API",
|
|
63
|
+
description: "{{description}}",
|
|
64
|
+
version: "1.0.0",
|
|
65
|
+
integrations: {
|
|
66
|
+
{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless") hasBetterAuthWithoutRest)}}
|
|
67
|
+
betterAuth: {
|
|
68
|
+
auth,
|
|
69
|
+
pathPrefix: "/api/auth",
|
|
70
|
+
},
|
|
71
|
+
{{/if}}
|
|
72
|
+
{{#if (includes apis "trpc")}}
|
|
73
|
+
trpc: {
|
|
74
|
+
routerFilePath: "./src/trpc/router.ts",
|
|
75
|
+
pathPrefix: "/trpc",
|
|
76
|
+
},
|
|
77
|
+
{{/if}}
|
|
78
|
+
zod: {
|
|
79
|
+
withFastify: {{#if (eq httpProvider "fastify")}}true{{else}}false{{/if}},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
SwaggerModule.setup(docsPath, app, document);
|
|
84
|
+
{{/if}}
|
|
85
|
+
Logger.log(`Swagger documentation available at http://localhost:${env.PORT ?? 3000}${docsPath}`, "SwaggerDocs");
|
|
86
|
+
{{/if}}
|
|
15
87
|
await app.listen(env.PORT ?? 3000);
|
|
88
|
+
Logger.log(`Started backend server on http://localhost:${env.PORT ?? 3000}/api`, "Main");
|
|
16
89
|
}
|
|
17
90
|
await bootstrap();
|