@malamute/ai-rules 1.0.0 → 1.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.
Files changed (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -4,260 +4,102 @@
4
4
 
5
5
  ## Stack
6
6
 
7
- - NestJS 10+ (latest stable)
7
+ - NestJS 11+
8
8
  - TypeScript strict mode
9
9
  - Node.js 20+
10
- - Jest for testing
11
- - Supertest for E2E
10
+ - Vitest + Supertest
11
+ - Prisma or TypeORM
12
12
 
13
- ## Architecture
14
-
15
- ### Modular Monolith (Recommended)
13
+ ## Architecture - Modular Monolith
16
14
 
17
15
  ```
18
16
  src/
19
17
  ├── modules/
20
- │ ├── users/
21
- │ │ ├── users.module.ts
22
- │ │ ├── users.controller.ts
23
- │ │ ├── users.service.ts
18
+ │ ├── [feature]/
19
+ │ │ ├── [feature].module.ts
20
+ │ │ ├── [feature].controller.ts
21
+ │ │ ├── [feature].service.ts
22
+ │ │ ├── [feature].repository.ts
24
23
  │ │ ├── dto/
25
- │ │ │ ├── create-user.dto.ts
26
- │ │ └── update-user.dto.ts
27
- ├── entities/
28
- │ │ └── user.entity.ts
29
- │ │ └── users.repository.ts
30
- │ │
31
- │ ├── auth/
32
- │ │ ├── auth.module.ts
33
- │ │ ├── auth.controller.ts
34
- │ │ ├── auth.service.ts
35
- │ │ ├── strategies/
36
- │ │ │ ├── jwt.strategy.ts
37
- │ │ │ └── local.strategy.ts
38
- │ │ └── guards/
39
- │ │ └── jwt-auth.guard.ts
40
- │ │
41
- │ └── [feature]/
42
- │ └── ...
43
-
24
+ │ │ └── entities/
25
+ │ └── auth/
26
+ ├── strategies/
27
+ └── guards/
44
28
  ├── common/
45
29
  │ ├── decorators/
46
30
  │ ├── filters/
47
31
  │ ├── guards/
48
32
  │ ├── interceptors/
49
33
  │ └── pipes/
50
-
51
34
  ├── config/
52
- │ ├── config.module.ts
53
- │ └── database.config.ts
54
-
55
35
  ├── app.module.ts
56
36
  └── main.ts
57
37
  ```
58
38
 
59
- ### Module Principles
60
-
61
- - **Single Responsibility**: Each module owns one domain
62
- - **Clear Boundaries**: Modules communicate via exported services
63
- - **In-Memory Communication**: Direct method calls, not HTTP between modules
64
- - **Barrel Exports**: Use `index.ts` for clean imports
39
+ ## Request Lifecycle
65
40
 
66
- ## Code Style
41
+ ```
42
+ Request → Middleware → Guard → Interceptor (pre) → Pipe → Controller → Interceptor (post) → Response
43
+ ```
67
44
 
68
- ### Controllers
45
+ ## Core Principles
69
46
 
70
- - Handle HTTP concerns only (request/response)
71
- - Delegate business logic to services
72
- - Use DTOs for all inputs
73
- - Return consistent response shapes
47
+ ### Module Design
74
48
 
75
- ```typescript
76
- @Controller('users')
77
- export class UsersController {
78
- constructor(private readonly usersService: UsersService) {}
49
+ - **Single Responsibility**: One module = one domain
50
+ - **Clear Boundaries**: Communicate via exported services only
51
+ - **Barrel Exports**: Use `index.ts` for clean imports
79
52
 
80
- @Post()
81
- @HttpCode(HttpStatus.CREATED)
82
- create(@Body() createUserDto: CreateUserDto): Promise<User> {
83
- return this.usersService.create(createUserDto);
84
- }
53
+ ### Layer Responsibilities
85
54
 
86
- @Get(':id')
87
- findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {
88
- return this.usersService.findOne(id);
89
- }
90
- }
91
- ```
55
+ | Layer | Responsibility |
56
+ |-------|---------------|
57
+ | Controller | HTTP only, delegates to service |
58
+ | Service | All business logic |
59
+ | Repository | Data access only |
60
+ | DTO | Validation with class-validator |
92
61
 
93
- ### Services
62
+ ### Exception Handling
94
63
 
95
- - Contain all business logic
96
- - Inject repositories/other services
97
- - Throw appropriate NestJS exceptions
64
+ Use built-in NestJS exceptions:
65
+ - `NotFoundException` - 404
66
+ - `BadRequestException` - 400
67
+ - `UnauthorizedException` - 401
68
+ - `ForbiddenException` - 403
69
+ - `ConflictException` - 409
98
70
 
99
- ```typescript
100
- @Injectable()
101
- export class UsersService {
102
- constructor(private readonly usersRepository: UsersRepository) {}
71
+ ### Validation (Global)
103
72
 
104
- async findOne(id: string): Promise<User> {
105
- const user = await this.usersRepository.findById(id);
106
- if (!user) {
107
- throw new NotFoundException(`User with ID ${id} not found`);
108
- }
109
- return user;
110
- }
111
- }
112
- ```
73
+ Enable global `ValidationPipe` in main.ts with:
74
+ - `whitelist: true` - Strip non-whitelisted properties
75
+ - `forbidNonWhitelisted: true` - Throw on extra properties
76
+ - `transform: true` - Auto-transform to DTO types
113
77
 
114
78
  ### DTOs
115
79
 
116
80
  - Always use class-validator decorators
117
- - Use class-transformer for transformation
118
- - Extend with PartialType/PickType/OmitType for variants
81
+ - `PartialType`, `PickType`, `OmitType` for variants
82
+ - Combine with Swagger decorators
119
83
 
120
- ```typescript
121
- export class CreateUserDto {
122
- @IsEmail()
123
- @IsNotEmpty()
124
- email: string;
84
+ ## Authentication
125
85
 
126
- @IsString()
127
- @MinLength(8)
128
- password: string;
129
-
130
- @IsString()
131
- @IsOptional()
132
- name?: string;
133
- }
134
-
135
- export class UpdateUserDto extends PartialType(CreateUserDto) {}
136
- ```
86
+ - Passport.js + JWT strategy
87
+ - Global `JwtAuthGuard` with `@Public()` decorator for exceptions
88
+ - `@CurrentUser()` decorator for accessing user
137
89
 
138
90
  ## Commands
139
91
 
140
92
  ```bash
141
- # Development
142
- npm run start:dev
143
-
144
- # Build
145
- npm run build
146
-
147
- # Tests
148
- npm run test # Unit tests
149
- npm run test:watch # Watch mode
150
- npm run test:cov # Coverage
151
- npm run test:e2e # E2E tests
152
-
153
- # Linting
154
- npm run lint
155
- npm run format
156
- ```
157
-
158
- ## Global Setup (main.ts)
159
-
160
- ```typescript
161
- async function bootstrap() {
162
- const app = await NestFactory.create(AppModule);
163
-
164
- // Global validation pipe
165
- app.useGlobalPipes(
166
- new ValidationPipe({
167
- whitelist: true,
168
- forbidNonWhitelisted: true,
169
- transform: true,
170
- transformOptions: {
171
- enableImplicitConversion: true,
172
- },
173
- }),
174
- );
175
-
176
- // Global prefix
177
- app.setGlobalPrefix('api/v1');
178
-
179
- // CORS
180
- app.enableCors();
181
-
182
- // Swagger (dev only)
183
- if (process.env.NODE_ENV !== 'production') {
184
- const config = new DocumentBuilder()
185
- .setTitle('API')
186
- .setVersion('1.0')
187
- .addBearerAuth()
188
- .build();
189
- const document = SwaggerModule.createDocument(app, config);
190
- SwaggerModule.setup('docs', app, document);
191
- }
192
-
193
- await app.listen(process.env.PORT ?? 3000);
194
- }
195
- ```
196
-
197
- ## Common Patterns
198
-
199
- ### Custom Decorator
200
-
201
- ```typescript
202
- // current-user.decorator.ts
203
- export const CurrentUser = createParamDecorator(
204
- (data: keyof User | undefined, ctx: ExecutionContext) => {
205
- const request = ctx.switchToHttp().getRequest();
206
- const user = request.user;
207
- return data ? user?.[data] : user;
208
- },
209
- );
210
-
211
- // Usage
212
- @Get('profile')
213
- getProfile(@CurrentUser() user: User) { ... }
93
+ npm run start:dev # Dev with watch
94
+ npm run build # Production build
95
+ npm run test # Unit tests
96
+ npm run test:e2e # E2E tests
97
+ npm run test:cov # Coverage
214
98
  ```
215
99
 
216
- ### Global Exception Filter
217
-
218
- ```typescript
219
- @Catch()
220
- export class AllExceptionsFilter implements ExceptionFilter {
221
- catch(exception: unknown, host: ArgumentsHost) {
222
- const ctx = host.switchToHttp();
223
- const response = ctx.getResponse<Response>();
224
-
225
- const status =
226
- exception instanceof HttpException
227
- ? exception.getStatus()
228
- : HttpStatus.INTERNAL_SERVER_ERROR;
229
-
230
- const message =
231
- exception instanceof HttpException
232
- ? exception.message
233
- : 'Internal server error';
234
-
235
- response.status(status).json({
236
- statusCode: status,
237
- message,
238
- timestamp: new Date().toISOString(),
239
- });
240
- }
241
- }
242
- ```
243
-
244
- ### Response Interceptor
100
+ ## Code Style
245
101
 
246
- ```typescript
247
- @Injectable()
248
- export class TransformInterceptor<T>
249
- implements NestInterceptor<T, Response<T>>
250
- {
251
- intercept(
252
- context: ExecutionContext,
253
- next: CallHandler,
254
- ): Observable<Response<T>> {
255
- return next.handle().pipe(
256
- map((data) => ({
257
- success: true,
258
- data,
259
- })),
260
- );
261
- }
262
- }
263
- ```
102
+ - Constructor injection (not property injection)
103
+ - `readonly` for injected dependencies
104
+ - Async methods return `Promise<T>`
105
+ - Use `ParseUUIDPipe`, `ParseIntPipe` for params
@@ -0,0 +1,358 @@
1
+ ---
2
+ paths:
3
+ - "app/api/**/*.ts"
4
+ - "src/app/api/**/*.ts"
5
+ ---
6
+
7
+ # Next.js API Routes (App Router)
8
+
9
+ ## Basic Route Handler
10
+
11
+ ```typescript
12
+ // app/api/users/route.ts
13
+ import { NextResponse } from 'next/server';
14
+
15
+ export async function GET() {
16
+ const users = await db.user.findMany();
17
+ return NextResponse.json(users);
18
+ }
19
+
20
+ export async function POST(request: Request) {
21
+ const body = await request.json();
22
+ const user = await db.user.create({ data: body });
23
+ return NextResponse.json(user, { status: 201 });
24
+ }
25
+ ```
26
+
27
+ ## Dynamic Routes
28
+
29
+ ```typescript
30
+ // app/api/users/[id]/route.ts
31
+ import { NextResponse } from 'next/server';
32
+
33
+ export async function GET(
34
+ request: Request,
35
+ { params }: { params: Promise<{ id: string }> }
36
+ ) {
37
+ const { id } = await params;
38
+ const user = await db.user.findUnique({ where: { id } });
39
+
40
+ if (!user) {
41
+ return NextResponse.json(
42
+ { error: 'User not found' },
43
+ { status: 404 }
44
+ );
45
+ }
46
+
47
+ return NextResponse.json(user);
48
+ }
49
+
50
+ export async function PUT(
51
+ request: Request,
52
+ { params }: { params: Promise<{ id: string }> }
53
+ ) {
54
+ const { id } = await params;
55
+ const body = await request.json();
56
+
57
+ const user = await db.user.update({
58
+ where: { id },
59
+ data: body,
60
+ });
61
+
62
+ return NextResponse.json(user);
63
+ }
64
+
65
+ export async function DELETE(
66
+ request: Request,
67
+ { params }: { params: Promise<{ id: string }> }
68
+ ) {
69
+ const { id } = await params;
70
+ await db.user.delete({ where: { id } });
71
+ return new NextResponse(null, { status: 204 });
72
+ }
73
+ ```
74
+
75
+ ## Request Handling
76
+
77
+ ### Query Parameters
78
+
79
+ ```typescript
80
+ export async function GET(request: Request) {
81
+ const { searchParams } = new URL(request.url);
82
+ const page = parseInt(searchParams.get('page') || '1');
83
+ const limit = parseInt(searchParams.get('limit') || '10');
84
+ const search = searchParams.get('search') || '';
85
+
86
+ const users = await db.user.findMany({
87
+ where: { name: { contains: search } },
88
+ skip: (page - 1) * limit,
89
+ take: limit,
90
+ });
91
+
92
+ const total = await db.user.count({
93
+ where: { name: { contains: search } },
94
+ });
95
+
96
+ return NextResponse.json({
97
+ data: users,
98
+ meta: {
99
+ page,
100
+ limit,
101
+ total,
102
+ totalPages: Math.ceil(total / limit),
103
+ },
104
+ });
105
+ }
106
+ ```
107
+
108
+ ### Headers & Cookies
109
+
110
+ ```typescript
111
+ import { cookies, headers } from 'next/headers';
112
+
113
+ export async function GET() {
114
+ const headersList = await headers();
115
+ const authHeader = headersList.get('authorization');
116
+
117
+ const cookieStore = await cookies();
118
+ const token = cookieStore.get('token');
119
+
120
+ return NextResponse.json({ auth: !!authHeader, hasToken: !!token });
121
+ }
122
+
123
+ export async function POST() {
124
+ const response = NextResponse.json({ success: true });
125
+
126
+ // Set cookie
127
+ response.cookies.set('session', 'value', {
128
+ httpOnly: true,
129
+ secure: process.env.NODE_ENV === 'production',
130
+ sameSite: 'lax',
131
+ maxAge: 60 * 60 * 24 * 7, // 1 week
132
+ });
133
+
134
+ return response;
135
+ }
136
+ ```
137
+
138
+ ## Validation with Zod
139
+
140
+ ```typescript
141
+ // app/api/users/route.ts
142
+ import { NextResponse } from 'next/server';
143
+ import { z } from 'zod';
144
+
145
+ const createUserSchema = z.object({
146
+ email: z.string().email(),
147
+ name: z.string().min(2).max(100),
148
+ age: z.number().min(18).optional(),
149
+ });
150
+
151
+ export async function POST(request: Request) {
152
+ const body = await request.json();
153
+
154
+ const result = createUserSchema.safeParse(body);
155
+
156
+ if (!result.success) {
157
+ return NextResponse.json(
158
+ {
159
+ type: 'validation_error',
160
+ title: 'Validation Error',
161
+ status: 400,
162
+ errors: result.error.issues.map(issue => ({
163
+ field: issue.path.join('.'),
164
+ message: issue.message,
165
+ })),
166
+ },
167
+ { status: 400 }
168
+ );
169
+ }
170
+
171
+ const user = await db.user.create({ data: result.data });
172
+ return NextResponse.json(user, { status: 201 });
173
+ }
174
+ ```
175
+
176
+ ## Error Handling
177
+
178
+ ```typescript
179
+ // lib/api-error.ts
180
+ export class ApiError extends Error {
181
+ constructor(
182
+ public status: number,
183
+ message: string,
184
+ public code?: string
185
+ ) {
186
+ super(message);
187
+ }
188
+ }
189
+
190
+ // app/api/users/[id]/route.ts
191
+ import { ApiError } from '@/lib/api-error';
192
+
193
+ export async function GET(
194
+ request: Request,
195
+ { params }: { params: Promise<{ id: string }> }
196
+ ) {
197
+ try {
198
+ const { id } = await params;
199
+ const user = await db.user.findUnique({ where: { id } });
200
+
201
+ if (!user) {
202
+ throw new ApiError(404, 'User not found', 'USER_NOT_FOUND');
203
+ }
204
+
205
+ return NextResponse.json(user);
206
+ } catch (error) {
207
+ if (error instanceof ApiError) {
208
+ return NextResponse.json(
209
+ {
210
+ type: `https://api.example.com/errors/${error.code}`,
211
+ title: error.message,
212
+ status: error.status,
213
+ },
214
+ { status: error.status }
215
+ );
216
+ }
217
+
218
+ console.error('Unexpected error:', error);
219
+ return NextResponse.json(
220
+ { title: 'Internal Server Error', status: 500 },
221
+ { status: 500 }
222
+ );
223
+ }
224
+ }
225
+ ```
226
+
227
+ ## Authentication
228
+
229
+ ```typescript
230
+ import { auth } from '@/auth';
231
+
232
+ export async function GET() {
233
+ const session = await auth();
234
+
235
+ if (!session) {
236
+ return NextResponse.json(
237
+ { error: 'Unauthorized' },
238
+ { status: 401 }
239
+ );
240
+ }
241
+
242
+ const users = await db.user.findMany({
243
+ where: { organizationId: session.user.organizationId },
244
+ });
245
+
246
+ return NextResponse.json(users);
247
+ }
248
+ ```
249
+
250
+ ## File Upload
251
+
252
+ ```typescript
253
+ // app/api/upload/route.ts
254
+ import { writeFile } from 'fs/promises';
255
+ import { NextResponse } from 'next/server';
256
+
257
+ export async function POST(request: Request) {
258
+ const formData = await request.formData();
259
+ const file = formData.get('file') as File | null;
260
+
261
+ if (!file) {
262
+ return NextResponse.json(
263
+ { error: 'No file uploaded' },
264
+ { status: 400 }
265
+ );
266
+ }
267
+
268
+ // Validate file
269
+ const maxSize = 5 * 1024 * 1024; // 5MB
270
+ if (file.size > maxSize) {
271
+ return NextResponse.json(
272
+ { error: 'File too large' },
273
+ { status: 400 }
274
+ );
275
+ }
276
+
277
+ const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
278
+ if (!allowedTypes.includes(file.type)) {
279
+ return NextResponse.json(
280
+ { error: 'Invalid file type' },
281
+ { status: 400 }
282
+ );
283
+ }
284
+
285
+ const bytes = await file.arrayBuffer();
286
+ const buffer = Buffer.from(bytes);
287
+
288
+ const filename = `${Date.now()}-${file.name}`;
289
+ await writeFile(`./public/uploads/${filename}`, buffer);
290
+
291
+ return NextResponse.json({ url: `/uploads/${filename}` });
292
+ }
293
+ ```
294
+
295
+ ## Streaming Response
296
+
297
+ ```typescript
298
+ export async function GET() {
299
+ const encoder = new TextEncoder();
300
+
301
+ const stream = new ReadableStream({
302
+ async start(controller) {
303
+ for (let i = 0; i < 10; i++) {
304
+ controller.enqueue(encoder.encode(`data: ${i}\n\n`));
305
+ await new Promise(resolve => setTimeout(resolve, 1000));
306
+ }
307
+ controller.close();
308
+ },
309
+ });
310
+
311
+ return new Response(stream, {
312
+ headers: {
313
+ 'Content-Type': 'text/event-stream',
314
+ 'Cache-Control': 'no-cache',
315
+ 'Connection': 'keep-alive',
316
+ },
317
+ });
318
+ }
319
+ ```
320
+
321
+ ## Route Configuration
322
+
323
+ ```typescript
324
+ // Caching
325
+ export const revalidate = 60; // Revalidate every 60 seconds
326
+ export const dynamic = 'force-dynamic'; // Always dynamic
327
+
328
+ // Runtime
329
+ export const runtime = 'edge'; // or 'nodejs'
330
+
331
+ // Max duration (Vercel)
332
+ export const maxDuration = 30;
333
+ ```
334
+
335
+ ## Anti-patterns
336
+
337
+ ```typescript
338
+ // BAD: Not validating input
339
+ export async function POST(request: Request) {
340
+ const body = await request.json();
341
+ await db.user.create({ data: body }); // SQL injection risk!
342
+ }
343
+
344
+ // GOOD: Validate with Zod
345
+ const result = schema.safeParse(body);
346
+ if (!result.success) return errorResponse(result.error);
347
+
348
+ // BAD: Exposing internal errors
349
+ catch (error) {
350
+ return NextResponse.json({ error: error.message }); // Leaks info!
351
+ }
352
+
353
+ // GOOD: Generic error message
354
+ catch (error) {
355
+ console.error(error);
356
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 });
357
+ }
358
+ ```