@mr.dj2u/knowledge 0.1.7 → 0.1.9
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/content/checklists/push-merge-loop.md +17 -17
- package/dist/content/checklists/unified-agent-bundle-validation.md +9 -9
- package/dist/content/examples/push-merge-loop.md +14 -14
- package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -8
- package/dist/content/guides/post-create-onboarding.md +150 -140
- package/dist/content/patterns/api/api-routes.md +313 -313
- package/dist/content/patterns/api/error-handling.md +310 -310
- package/dist/content/patterns/database/drizzle-schema.md +279 -279
- package/dist/content/patterns/database/migrations.md +364 -364
- package/dist/content/patterns/database/query-organization.md +536 -536
- package/dist/content/patterns/database/relations.md +449 -449
- package/dist/content/patterns/deployment/build-configuration.md +440 -440
- package/dist/content/patterns/deployment/ci-cd-patterns.md +447 -447
- package/dist/content/patterns/deployment/environment-config.md +379 -379
- package/dist/content/patterns/deployment/hosting-setup.md +424 -424
- package/dist/content/patterns/project/configuration-patterns.md +459 -459
- package/dist/content/patterns/project/documentation-org.md +506 -506
- package/dist/content/patterns/project/folder-structure.md +397 -397
- package/dist/content/patterns/project/library-exports.md +464 -464
- package/dist/content/patterns/project/monorepo-structure.md +500 -500
- package/dist/content/patterns/routing/dynamic-routes.md +220 -220
- package/dist/content/patterns/routing/file-based-routing.md +185 -185
- package/dist/content/patterns/routing/route-groups.md +428 -428
- package/dist/content/patterns/state/persistence-middleware.md +520 -520
- package/dist/content/patterns/state/selector-hooks.md +537 -537
- package/dist/content/patterns/state/store-organization.md +538 -538
- package/dist/content/patterns/state/zustand-patterns.md +347 -347
- package/dist/content/patterns/styling/component-styling.md +467 -467
- package/dist/content/patterns/styling/responsive-patterns.md +397 -397
- package/dist/content/patterns/styling/theme-configuration.md +425 -425
- package/dist/content/patterns/styling/uniwind-setup.md +411 -411
- package/dist/content/prompts/continue-development.md +41 -35
- package/dist/content/prompts/create-expo-super-stack.md +4 -1
- package/dist/content/prompts/fix-seo.md +29 -29
- package/dist/content/prompts/onboard-new-expo-app.md +11 -11
- package/dist/content/prompts/prepare-deploy.md +29 -29
- package/dist/content/prompts/project-research-plan.md +29 -29
- package/dist/content/prompts/push-merge-loop.md +25 -25
- package/dist/content/prompts/retrospective-project-onboarding.md +23 -0
- package/dist/content/prompts/review-expo-project.md +29 -29
- package/dist/content/prompts/run-doctor.md +38 -38
- package/dist/content/prompts/wrap-up.md +70 -67
- package/dist/content/reference/create-expo-stack-uniwind.md +29 -29
- package/dist/content/reference/doctor-dogfood.md +1 -1
- package/dist/content/reference/mcp-sdk-transport.md +30 -30
- package/dist/content/reference/reference-repo-evacuation.md +31 -31
- package/dist/content/resource-index.json +1 -0
- package/dist/content/rules/env-hygiene.md +10 -0
- package/dist/content/rules/ssr-safety.md +7 -0
- package/dist/content/skills/api-routes.md +34 -33
- package/dist/content/skills/continue-development.md +49 -32
- package/dist/content/skills/debugging.md +32 -32
- package/dist/content/skills/deployment.md +32 -32
- package/dist/content/skills/dev-server-management.md +32 -32
- package/dist/content/skills/env-vars.md +32 -32
- package/dist/content/skills/expo-router-architecture.md +34 -33
- package/dist/content/skills/expo-ssr-safety.md +32 -32
- package/dist/content/skills/plugin-creation.md +41 -41
- package/dist/content/skills/production-server-patterns.md +31 -31
- package/dist/content/skills/project-onboarding.md +35 -31
- package/dist/content/skills/research-plan-intake.md +32 -32
- package/dist/content/skills/seo-metadata.md +31 -31
- package/dist/content/skills/super-stack-startup.md +38 -34
- package/dist/content/skills/uniwind-theming.md +32 -32
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +11 -0
- package/dist/prompts/index.js.map +1 -1
- package/package.json +7 -1
|
@@ -1,311 +1,311 @@
|
|
|
1
|
-
# Error Handling in API Services
|
|
2
|
-
|
|
3
|
-
## Description
|
|
4
|
-
|
|
5
|
-
Structured error handling in API services uses custom error classes with status codes, error details, and proper HTTP response formatting. This ensures consistent error responses across all API endpoints with full context for debugging and client-side error handling.
|
|
6
|
-
|
|
7
|
-
## When to Use
|
|
8
|
-
|
|
9
|
-
**Use custom error handling** for:
|
|
10
|
-
- ✅ API endpoints that need consistent error responses
|
|
11
|
-
- ✅ Services that interact with external APIs or databases
|
|
12
|
-
- ✅ Situations where you need to distinguish error types and statuses
|
|
13
|
-
- ✅ Client-side error handling with structured error objects
|
|
14
|
-
|
|
15
|
-
## Code Example
|
|
16
|
-
|
|
17
|
-
### Custom Error Class Definition
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
// File: src/services/quantum-key-management.ts
|
|
21
|
-
export class QuantumApiError extends Error {
|
|
22
|
-
status: number;
|
|
23
|
-
details?: unknown;
|
|
24
|
-
|
|
25
|
-
constructor(message: string, status: number, details?: unknown) {
|
|
26
|
-
super(message);
|
|
27
|
-
this.name = 'QuantumApiError';
|
|
28
|
-
this.status = status;
|
|
29
|
-
this.details = details;
|
|
30
|
-
|
|
31
|
-
// Maintain proper prototype chain for instanceof checks
|
|
32
|
-
Object.setPrototypeOf(this, QuantumApiError.prototype);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Usage in services
|
|
37
|
-
async function fetchQuantumProfile(
|
|
38
|
-
bearerClient: QuantumApiClient,
|
|
39
|
-
profileId: string
|
|
40
|
-
): Promise<IbmProfileRecord> {
|
|
41
|
-
try {
|
|
42
|
-
const response = await bearerClient.getProfile(profileId);
|
|
43
|
-
return response;
|
|
44
|
-
} catch (err) {
|
|
45
|
-
if (err instanceof SdkQuantumApiError) {
|
|
46
|
-
throw new QuantumApiError(
|
|
47
|
-
`Failed to fetch profile: ${err.message}`,
|
|
48
|
-
err.statusCode || 500,
|
|
49
|
-
{ originalError: err }
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
throw new QuantumApiError(
|
|
53
|
-
'Unknown error fetching profile',
|
|
54
|
-
500,
|
|
55
|
-
{ originalError: err }
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**From:** DJsPortfolio/src/services/quantum-key-management.ts (lines 1-60)
|
|
62
|
-
|
|
63
|
-
### SDK Client Error Mapping
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
// File: src/lib/quantum-sdk-executor.ts
|
|
67
|
-
import { QuantumApiError as SdkQuantumApiError } from '@mr.dj2u/quantum-api';
|
|
68
|
-
|
|
69
|
-
export type QuantumSdkEndpointExecutionResult = {
|
|
70
|
-
status: number;
|
|
71
|
-
statusText: string;
|
|
72
|
-
data: unknown;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
async function executeQuantumEndpoint(
|
|
76
|
-
input: QuantumSdkEndpointExecutionInput
|
|
77
|
-
): Promise<QuantumSdkEndpointExecutionResult> {
|
|
78
|
-
try {
|
|
79
|
-
// Select appropriate client based on auth method
|
|
80
|
-
const client = input.bearerToken
|
|
81
|
-
? createQuantumBearerClient(input.baseUrl, input.bearerToken)
|
|
82
|
-
: createQuantumPublicClient(input.baseUrl);
|
|
83
|
-
|
|
84
|
-
// Execute request
|
|
85
|
-
const response = await client.request({
|
|
86
|
-
method: input.method,
|
|
87
|
-
path: input.path,
|
|
88
|
-
body: input.body,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
status: response.status,
|
|
93
|
-
statusText: response.statusText,
|
|
94
|
-
data: response.data,
|
|
95
|
-
};
|
|
96
|
-
} catch (err) {
|
|
97
|
-
if (err instanceof SdkQuantumApiError) {
|
|
98
|
-
return {
|
|
99
|
-
status: err.statusCode || 500,
|
|
100
|
-
statusText: 'Error',
|
|
101
|
-
data: {
|
|
102
|
-
error: err.message,
|
|
103
|
-
details: err.details,
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
status: 500,
|
|
110
|
-
statusText: 'Internal Server Error',
|
|
111
|
-
data: {
|
|
112
|
-
error: 'Unexpected error executing endpoint',
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**From:** DJsPortfolio/src/lib/quantum-sdk-executor.ts (lines 1-50)
|
|
120
|
-
|
|
121
|
-
### Error Response Format
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// Standard error response structure
|
|
125
|
-
type ErrorResponse = {
|
|
126
|
-
error: string; // Human-readable error message
|
|
127
|
-
status: number; // HTTP status code
|
|
128
|
-
details?: unknown; // Additional error context
|
|
129
|
-
timestamp?: string; // When error occurred
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Return from API route
|
|
133
|
-
export async function POST(request: ExpoRequest) {
|
|
134
|
-
try {
|
|
135
|
-
const data = await request.json();
|
|
136
|
-
const result = await processQuantumRequest(data);
|
|
137
|
-
return new ExpoResponse(JSON.stringify(result), { status: 200 });
|
|
138
|
-
} catch (err) {
|
|
139
|
-
const errorResponse: ErrorResponse = {
|
|
140
|
-
error: err instanceof Error ? err.message : 'Unknown error',
|
|
141
|
-
status: err instanceof QuantumApiError ? err.status : 500,
|
|
142
|
-
details: err instanceof QuantumApiError ? err.details : undefined,
|
|
143
|
-
timestamp: new Date().toISOString(),
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
return new ExpoResponse(
|
|
147
|
-
JSON.stringify(errorResponse),
|
|
148
|
-
{
|
|
149
|
-
status: errorResponse.status,
|
|
150
|
-
headers: { 'Content-Type': 'application/json' },
|
|
151
|
-
}
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Configuration
|
|
158
|
-
|
|
159
|
-
### Environment-Specific Error Handling
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
// config/error-handling.ts
|
|
163
|
-
export const errorConfig = {
|
|
164
|
-
development: {
|
|
165
|
-
exposeStackTraces: true,
|
|
166
|
-
logErrors: true,
|
|
167
|
-
verboseMessages: true,
|
|
168
|
-
},
|
|
169
|
-
production: {
|
|
170
|
-
exposeStackTraces: false,
|
|
171
|
-
logErrors: true,
|
|
172
|
-
verboseMessages: false,
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
export function shouldExposeErrorDetails(): boolean {
|
|
177
|
-
return errorConfig[process.env.NODE_ENV || 'development'].exposeStackTraces;
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Error Logging
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
// middleware/error-logger.ts
|
|
185
|
-
export function logError(err: Error, context?: Record<string, any>) {
|
|
186
|
-
const timestamp = new Date().toISOString();
|
|
187
|
-
const errorData = {
|
|
188
|
-
timestamp,
|
|
189
|
-
message: err.message,
|
|
190
|
-
name: err.name,
|
|
191
|
-
stack: err.stack,
|
|
192
|
-
context,
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
console.error(JSON.stringify(errorData));
|
|
196
|
-
|
|
197
|
-
// Could also send to external logging service
|
|
198
|
-
// sendToSentry(errorData);
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
## Best Practices
|
|
203
|
-
|
|
204
|
-
### ✅ DO
|
|
205
|
-
|
|
206
|
-
1. **Create typed error classes** for different error scenarios
|
|
207
|
-
```typescript
|
|
208
|
-
class ValidationError extends Error {
|
|
209
|
-
constructor(public field: string, message: string) {
|
|
210
|
-
super(message);
|
|
211
|
-
this.name = 'ValidationError';
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
class AuthenticationError extends Error {
|
|
216
|
-
constructor(message: string) {
|
|
217
|
-
super(message);
|
|
218
|
-
this.name = 'AuthenticationError';
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
2. **Include status codes** with all errors
|
|
224
|
-
```typescript
|
|
225
|
-
throw new ValidationError('email', 'Invalid email format');
|
|
226
|
-
// Map to 400 status in handler
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
3. **Log errors with context** for debugging
|
|
230
|
-
```typescript
|
|
231
|
-
try {
|
|
232
|
-
await fetchData();
|
|
233
|
-
} catch (err) {
|
|
234
|
-
logError(err, {
|
|
235
|
-
userId: user.id,
|
|
236
|
-
action: 'fetch-profile',
|
|
237
|
-
timestamp: Date.now(),
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
4. **Distinguish error types** in client responses
|
|
243
|
-
```typescript
|
|
244
|
-
const statusMap = {
|
|
245
|
-
ValidationError: 400,
|
|
246
|
-
AuthenticationError: 401,
|
|
247
|
-
NotFoundError: 404,
|
|
248
|
-
ServerError: 500,
|
|
249
|
-
};
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### ❌ DON'T
|
|
253
|
-
|
|
254
|
-
1. **Don't expose internal error details** in production
|
|
255
|
-
```typescript
|
|
256
|
-
// ❌ BAD - leaks implementation details
|
|
257
|
-
return JSON.stringify({ error: err.stack });
|
|
258
|
-
|
|
259
|
-
// ✅ GOOD - generic message, log details server-side
|
|
260
|
-
return JSON.stringify({ error: 'Internal server error' });
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
2. **Don't ignore error types** from third-party libraries
|
|
264
|
-
```typescript
|
|
265
|
-
// ❌ BAD - loses error context
|
|
266
|
-
catch (err) {
|
|
267
|
-
throw new Error('Failed');
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// ✅ GOOD - map SDK errors to application errors
|
|
271
|
-
catch (err) {
|
|
272
|
-
if (err instanceof SdkError) {
|
|
273
|
-
throw new ApiError(err.message, err.statusCode);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
3. **Don't return different error formats** across endpoints
|
|
279
|
-
```typescript
|
|
280
|
-
// ❌ INCONSISTENT
|
|
281
|
-
Endpoint 1: { message: "Error" }
|
|
282
|
-
Endpoint 2: { error: "Error" }
|
|
283
|
-
Endpoint 3: { err: "Error" }
|
|
284
|
-
|
|
285
|
-
// ✅ CONSISTENT
|
|
286
|
-
All endpoints: { error, status, details, timestamp }
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
4. **Don't forget to set proper HTTP status codes**
|
|
290
|
-
```typescript
|
|
291
|
-
// ❌ BAD - always returns 200
|
|
292
|
-
return new ExpoResponse(JSON.stringify(error), { status: 200 });
|
|
293
|
-
|
|
294
|
-
// ✅ GOOD - correct status codes
|
|
295
|
-
return new ExpoResponse(
|
|
296
|
-
JSON.stringify(error),
|
|
297
|
-
{ status: error.status || 500 }
|
|
298
|
-
);
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## Related Patterns
|
|
302
|
-
|
|
303
|
-
- [API Routes](./api-routes.md) — Route endpoint structure
|
|
304
|
-
- [CORS Configuration](./cors-configuration.md) — Origin validation
|
|
305
|
-
- [Health Endpoints](./health-endpoints.md) — Health check patterns
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
*Pattern extracted from production repositories: DJsPortfolio, PokePages*
|
|
310
|
-
*Files: DJsPortfolio/src\services\quantum-key-management.ts*
|
|
1
|
+
# Error Handling in API Services
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Structured error handling in API services uses custom error classes with status codes, error details, and proper HTTP response formatting. This ensures consistent error responses across all API endpoints with full context for debugging and client-side error handling.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
**Use custom error handling** for:
|
|
10
|
+
- ✅ API endpoints that need consistent error responses
|
|
11
|
+
- ✅ Services that interact with external APIs or databases
|
|
12
|
+
- ✅ Situations where you need to distinguish error types and statuses
|
|
13
|
+
- ✅ Client-side error handling with structured error objects
|
|
14
|
+
|
|
15
|
+
## Code Example
|
|
16
|
+
|
|
17
|
+
### Custom Error Class Definition
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// File: src/services/quantum-key-management.ts
|
|
21
|
+
export class QuantumApiError extends Error {
|
|
22
|
+
status: number;
|
|
23
|
+
details?: unknown;
|
|
24
|
+
|
|
25
|
+
constructor(message: string, status: number, details?: unknown) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'QuantumApiError';
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.details = details;
|
|
30
|
+
|
|
31
|
+
// Maintain proper prototype chain for instanceof checks
|
|
32
|
+
Object.setPrototypeOf(this, QuantumApiError.prototype);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Usage in services
|
|
37
|
+
async function fetchQuantumProfile(
|
|
38
|
+
bearerClient: QuantumApiClient,
|
|
39
|
+
profileId: string
|
|
40
|
+
): Promise<IbmProfileRecord> {
|
|
41
|
+
try {
|
|
42
|
+
const response = await bearerClient.getProfile(profileId);
|
|
43
|
+
return response;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
if (err instanceof SdkQuantumApiError) {
|
|
46
|
+
throw new QuantumApiError(
|
|
47
|
+
`Failed to fetch profile: ${err.message}`,
|
|
48
|
+
err.statusCode || 500,
|
|
49
|
+
{ originalError: err }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
throw new QuantumApiError(
|
|
53
|
+
'Unknown error fetching profile',
|
|
54
|
+
500,
|
|
55
|
+
{ originalError: err }
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**From:** DJsPortfolio/src/services/quantum-key-management.ts (lines 1-60)
|
|
62
|
+
|
|
63
|
+
### SDK Client Error Mapping
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// File: src/lib/quantum-sdk-executor.ts
|
|
67
|
+
import { QuantumApiError as SdkQuantumApiError } from '@mr.dj2u/quantum-api';
|
|
68
|
+
|
|
69
|
+
export type QuantumSdkEndpointExecutionResult = {
|
|
70
|
+
status: number;
|
|
71
|
+
statusText: string;
|
|
72
|
+
data: unknown;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
async function executeQuantumEndpoint(
|
|
76
|
+
input: QuantumSdkEndpointExecutionInput
|
|
77
|
+
): Promise<QuantumSdkEndpointExecutionResult> {
|
|
78
|
+
try {
|
|
79
|
+
// Select appropriate client based on auth method
|
|
80
|
+
const client = input.bearerToken
|
|
81
|
+
? createQuantumBearerClient(input.baseUrl, input.bearerToken)
|
|
82
|
+
: createQuantumPublicClient(input.baseUrl);
|
|
83
|
+
|
|
84
|
+
// Execute request
|
|
85
|
+
const response = await client.request({
|
|
86
|
+
method: input.method,
|
|
87
|
+
path: input.path,
|
|
88
|
+
body: input.body,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
status: response.status,
|
|
93
|
+
statusText: response.statusText,
|
|
94
|
+
data: response.data,
|
|
95
|
+
};
|
|
96
|
+
} catch (err) {
|
|
97
|
+
if (err instanceof SdkQuantumApiError) {
|
|
98
|
+
return {
|
|
99
|
+
status: err.statusCode || 500,
|
|
100
|
+
statusText: 'Error',
|
|
101
|
+
data: {
|
|
102
|
+
error: err.message,
|
|
103
|
+
details: err.details,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
status: 500,
|
|
110
|
+
statusText: 'Internal Server Error',
|
|
111
|
+
data: {
|
|
112
|
+
error: 'Unexpected error executing endpoint',
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**From:** DJsPortfolio/src/lib/quantum-sdk-executor.ts (lines 1-50)
|
|
120
|
+
|
|
121
|
+
### Error Response Format
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Standard error response structure
|
|
125
|
+
type ErrorResponse = {
|
|
126
|
+
error: string; // Human-readable error message
|
|
127
|
+
status: number; // HTTP status code
|
|
128
|
+
details?: unknown; // Additional error context
|
|
129
|
+
timestamp?: string; // When error occurred
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Return from API route
|
|
133
|
+
export async function POST(request: ExpoRequest) {
|
|
134
|
+
try {
|
|
135
|
+
const data = await request.json();
|
|
136
|
+
const result = await processQuantumRequest(data);
|
|
137
|
+
return new ExpoResponse(JSON.stringify(result), { status: 200 });
|
|
138
|
+
} catch (err) {
|
|
139
|
+
const errorResponse: ErrorResponse = {
|
|
140
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
141
|
+
status: err instanceof QuantumApiError ? err.status : 500,
|
|
142
|
+
details: err instanceof QuantumApiError ? err.details : undefined,
|
|
143
|
+
timestamp: new Date().toISOString(),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return new ExpoResponse(
|
|
147
|
+
JSON.stringify(errorResponse),
|
|
148
|
+
{
|
|
149
|
+
status: errorResponse.status,
|
|
150
|
+
headers: { 'Content-Type': 'application/json' },
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
### Environment-Specific Error Handling
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// config/error-handling.ts
|
|
163
|
+
export const errorConfig = {
|
|
164
|
+
development: {
|
|
165
|
+
exposeStackTraces: true,
|
|
166
|
+
logErrors: true,
|
|
167
|
+
verboseMessages: true,
|
|
168
|
+
},
|
|
169
|
+
production: {
|
|
170
|
+
exposeStackTraces: false,
|
|
171
|
+
logErrors: true,
|
|
172
|
+
verboseMessages: false,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export function shouldExposeErrorDetails(): boolean {
|
|
177
|
+
return errorConfig[process.env.NODE_ENV || 'development'].exposeStackTraces;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Error Logging
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// middleware/error-logger.ts
|
|
185
|
+
export function logError(err: Error, context?: Record<string, any>) {
|
|
186
|
+
const timestamp = new Date().toISOString();
|
|
187
|
+
const errorData = {
|
|
188
|
+
timestamp,
|
|
189
|
+
message: err.message,
|
|
190
|
+
name: err.name,
|
|
191
|
+
stack: err.stack,
|
|
192
|
+
context,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
console.error(JSON.stringify(errorData));
|
|
196
|
+
|
|
197
|
+
// Could also send to external logging service
|
|
198
|
+
// sendToSentry(errorData);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Best Practices
|
|
203
|
+
|
|
204
|
+
### ✅ DO
|
|
205
|
+
|
|
206
|
+
1. **Create typed error classes** for different error scenarios
|
|
207
|
+
```typescript
|
|
208
|
+
class ValidationError extends Error {
|
|
209
|
+
constructor(public field: string, message: string) {
|
|
210
|
+
super(message);
|
|
211
|
+
this.name = 'ValidationError';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
class AuthenticationError extends Error {
|
|
216
|
+
constructor(message: string) {
|
|
217
|
+
super(message);
|
|
218
|
+
this.name = 'AuthenticationError';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
2. **Include status codes** with all errors
|
|
224
|
+
```typescript
|
|
225
|
+
throw new ValidationError('email', 'Invalid email format');
|
|
226
|
+
// Map to 400 status in handler
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
3. **Log errors with context** for debugging
|
|
230
|
+
```typescript
|
|
231
|
+
try {
|
|
232
|
+
await fetchData();
|
|
233
|
+
} catch (err) {
|
|
234
|
+
logError(err, {
|
|
235
|
+
userId: user.id,
|
|
236
|
+
action: 'fetch-profile',
|
|
237
|
+
timestamp: Date.now(),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
4. **Distinguish error types** in client responses
|
|
243
|
+
```typescript
|
|
244
|
+
const statusMap = {
|
|
245
|
+
ValidationError: 400,
|
|
246
|
+
AuthenticationError: 401,
|
|
247
|
+
NotFoundError: 404,
|
|
248
|
+
ServerError: 500,
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### ❌ DON'T
|
|
253
|
+
|
|
254
|
+
1. **Don't expose internal error details** in production
|
|
255
|
+
```typescript
|
|
256
|
+
// ❌ BAD - leaks implementation details
|
|
257
|
+
return JSON.stringify({ error: err.stack });
|
|
258
|
+
|
|
259
|
+
// ✅ GOOD - generic message, log details server-side
|
|
260
|
+
return JSON.stringify({ error: 'Internal server error' });
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
2. **Don't ignore error types** from third-party libraries
|
|
264
|
+
```typescript
|
|
265
|
+
// ❌ BAD - loses error context
|
|
266
|
+
catch (err) {
|
|
267
|
+
throw new Error('Failed');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ✅ GOOD - map SDK errors to application errors
|
|
271
|
+
catch (err) {
|
|
272
|
+
if (err instanceof SdkError) {
|
|
273
|
+
throw new ApiError(err.message, err.statusCode);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
3. **Don't return different error formats** across endpoints
|
|
279
|
+
```typescript
|
|
280
|
+
// ❌ INCONSISTENT
|
|
281
|
+
Endpoint 1: { message: "Error" }
|
|
282
|
+
Endpoint 2: { error: "Error" }
|
|
283
|
+
Endpoint 3: { err: "Error" }
|
|
284
|
+
|
|
285
|
+
// ✅ CONSISTENT
|
|
286
|
+
All endpoints: { error, status, details, timestamp }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
4. **Don't forget to set proper HTTP status codes**
|
|
290
|
+
```typescript
|
|
291
|
+
// ❌ BAD - always returns 200
|
|
292
|
+
return new ExpoResponse(JSON.stringify(error), { status: 200 });
|
|
293
|
+
|
|
294
|
+
// ✅ GOOD - correct status codes
|
|
295
|
+
return new ExpoResponse(
|
|
296
|
+
JSON.stringify(error),
|
|
297
|
+
{ status: error.status || 500 }
|
|
298
|
+
);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Related Patterns
|
|
302
|
+
|
|
303
|
+
- [API Routes](./api-routes.md) — Route endpoint structure
|
|
304
|
+
- [CORS Configuration](./cors-configuration.md) — Origin validation
|
|
305
|
+
- [Health Endpoints](./health-endpoints.md) — Health check patterns
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
*Pattern extracted from production repositories: DJsPortfolio, PokePages*
|
|
310
|
+
*Files: DJsPortfolio/src\services\quantum-key-management.ts*
|
|
311
311
|
*Lines 1-60 showing custom QuantumApiError class and error handling patterns*
|