@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.
Files changed (68) hide show
  1. package/dist/content/checklists/push-merge-loop.md +17 -17
  2. package/dist/content/checklists/unified-agent-bundle-validation.md +9 -9
  3. package/dist/content/examples/push-merge-loop.md +14 -14
  4. package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -8
  5. package/dist/content/guides/post-create-onboarding.md +150 -140
  6. package/dist/content/patterns/api/api-routes.md +313 -313
  7. package/dist/content/patterns/api/error-handling.md +310 -310
  8. package/dist/content/patterns/database/drizzle-schema.md +279 -279
  9. package/dist/content/patterns/database/migrations.md +364 -364
  10. package/dist/content/patterns/database/query-organization.md +536 -536
  11. package/dist/content/patterns/database/relations.md +449 -449
  12. package/dist/content/patterns/deployment/build-configuration.md +440 -440
  13. package/dist/content/patterns/deployment/ci-cd-patterns.md +447 -447
  14. package/dist/content/patterns/deployment/environment-config.md +379 -379
  15. package/dist/content/patterns/deployment/hosting-setup.md +424 -424
  16. package/dist/content/patterns/project/configuration-patterns.md +459 -459
  17. package/dist/content/patterns/project/documentation-org.md +506 -506
  18. package/dist/content/patterns/project/folder-structure.md +397 -397
  19. package/dist/content/patterns/project/library-exports.md +464 -464
  20. package/dist/content/patterns/project/monorepo-structure.md +500 -500
  21. package/dist/content/patterns/routing/dynamic-routes.md +220 -220
  22. package/dist/content/patterns/routing/file-based-routing.md +185 -185
  23. package/dist/content/patterns/routing/route-groups.md +428 -428
  24. package/dist/content/patterns/state/persistence-middleware.md +520 -520
  25. package/dist/content/patterns/state/selector-hooks.md +537 -537
  26. package/dist/content/patterns/state/store-organization.md +538 -538
  27. package/dist/content/patterns/state/zustand-patterns.md +347 -347
  28. package/dist/content/patterns/styling/component-styling.md +467 -467
  29. package/dist/content/patterns/styling/responsive-patterns.md +397 -397
  30. package/dist/content/patterns/styling/theme-configuration.md +425 -425
  31. package/dist/content/patterns/styling/uniwind-setup.md +411 -411
  32. package/dist/content/prompts/continue-development.md +41 -35
  33. package/dist/content/prompts/create-expo-super-stack.md +4 -1
  34. package/dist/content/prompts/fix-seo.md +29 -29
  35. package/dist/content/prompts/onboard-new-expo-app.md +11 -11
  36. package/dist/content/prompts/prepare-deploy.md +29 -29
  37. package/dist/content/prompts/project-research-plan.md +29 -29
  38. package/dist/content/prompts/push-merge-loop.md +25 -25
  39. package/dist/content/prompts/retrospective-project-onboarding.md +23 -0
  40. package/dist/content/prompts/review-expo-project.md +29 -29
  41. package/dist/content/prompts/run-doctor.md +38 -38
  42. package/dist/content/prompts/wrap-up.md +70 -67
  43. package/dist/content/reference/create-expo-stack-uniwind.md +29 -29
  44. package/dist/content/reference/doctor-dogfood.md +1 -1
  45. package/dist/content/reference/mcp-sdk-transport.md +30 -30
  46. package/dist/content/reference/reference-repo-evacuation.md +31 -31
  47. package/dist/content/resource-index.json +1 -0
  48. package/dist/content/rules/env-hygiene.md +10 -0
  49. package/dist/content/rules/ssr-safety.md +7 -0
  50. package/dist/content/skills/api-routes.md +34 -33
  51. package/dist/content/skills/continue-development.md +49 -32
  52. package/dist/content/skills/debugging.md +32 -32
  53. package/dist/content/skills/deployment.md +32 -32
  54. package/dist/content/skills/dev-server-management.md +32 -32
  55. package/dist/content/skills/env-vars.md +32 -32
  56. package/dist/content/skills/expo-router-architecture.md +34 -33
  57. package/dist/content/skills/expo-ssr-safety.md +32 -32
  58. package/dist/content/skills/plugin-creation.md +41 -41
  59. package/dist/content/skills/production-server-patterns.md +31 -31
  60. package/dist/content/skills/project-onboarding.md +35 -31
  61. package/dist/content/skills/research-plan-intake.md +32 -32
  62. package/dist/content/skills/seo-metadata.md +31 -31
  63. package/dist/content/skills/super-stack-startup.md +38 -34
  64. package/dist/content/skills/uniwind-theming.md +32 -32
  65. package/dist/prompts/index.d.ts.map +1 -1
  66. package/dist/prompts/index.js +11 -0
  67. package/dist/prompts/index.js.map +1 -1
  68. package/package.json +7 -1
@@ -1,314 +1,314 @@
1
- # API Routes with Catch-All Pattern
2
-
3
- ## Description
4
-
5
- Expo Router API routes use the `+api.ts` pattern to create server-side endpoints directly alongside client routes. The `[...segments]+api.ts` syntax creates catch-all routes that capture all path segments, enabling flexible API endpoint structures with full control over HTTP methods, CORS, and request handling.
6
-
7
- ## When to Use
8
-
9
- **Use API routes** for:
10
- - ✅ Backend endpoints served from same domain as frontend
11
- - ✅ Proxy servers that forward requests upstream
12
- - ✅ Server-side validation and data transformation
13
- - ✅ Protected endpoints with authentication
14
- - ✅ Catch-all routes for dynamic path handling
15
-
16
- ## Code Example
17
-
18
- ### Basic API Route (Single Endpoint)
19
-
20
- ```typescript
21
- // File: app/api/quantum-backend/[...segments]+api.ts
22
- import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
23
-
24
- type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
25
-
26
- const ALLOWED_ORIGINS = [
27
- 'http://localhost:3000',
28
- 'http://localhost:8081',
29
- 'http://127.0.0.1:3000',
30
- 'http://127.0.0.1:8081',
31
- ];
32
-
33
- function normalizeOrigin(request: ExpoRequest): string | null {
34
- const origin = request.headers.get('origin');
35
- if (!origin) return null;
36
-
37
- return ALLOWED_ORIGINS.includes(origin) ? origin : null;
38
- }
39
-
40
- export async function GET(request: ExpoRequest) {
41
- const allowedOrigin = normalizeOrigin(request);
42
-
43
- if (request.method === 'OPTIONS') {
44
- return new ExpoResponse(null, {
45
- status: 204,
46
- headers: allowedOrigin
47
- ? { 'Access-Control-Allow-Origin': allowedOrigin }
48
- : {},
49
- });
50
- }
51
-
52
- // Handle GET request
53
- return new ExpoResponse(JSON.stringify({ message: 'GET OK' }), {
54
- status: 200,
55
- headers: {
56
- 'Content-Type': 'application/json',
57
- ...(allowedOrigin && {
58
- 'Access-Control-Allow-Origin': allowedOrigin,
59
- }),
60
- },
61
- });
62
- }
63
-
64
- export async function POST(request: ExpoRequest) {
65
- const allowedOrigin = normalizeOrigin(request);
66
- const body = await request.json();
67
-
68
- // Process request
69
- const result = {
70
- success: true,
71
- data: body,
72
- };
73
-
74
- return new ExpoResponse(JSON.stringify(result), {
75
- status: 200,
76
- headers: {
77
- 'Content-Type': 'application/json',
78
- ...(allowedOrigin && {
79
- 'Access-Control-Allow-Origin': allowedOrigin,
80
- }),
81
- },
82
- });
83
- }
84
- ```
85
-
86
- **From:** DJsPortfolio/src/app/api/quantum-backend/[...segments]+api.ts
87
-
88
- ### Catch-All Route with Path Normalization
89
-
90
- ```typescript
91
- // File: app/public-facing/api/quantum/v1/[...segments]+api.ts
92
- import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
93
-
94
- const DISALLOWED_PREFIXES = ['/v1/keys', '/v1/ibm/profiles'];
95
- const DEFAULT_UPSTREAM_BASE_URL = 'http://127.0.0.1:8000/v1';
96
-
97
- type ProxyMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
98
-
99
- function normalizeUpstreamBaseUrl(): string {
100
- const raw = process.env.EXPO_PUBLIC_QUANTUM_API_BASE_URL?.trim();
101
- const base = raw || DEFAULT_UPSTREAM_BASE_URL;
102
-
103
- // Remove trailing slash
104
- const trimmed = base.replace(/\/+$/, '');
105
-
106
- // Ensure /v1 suffix
107
- return trimmed.endsWith('/v1') ? trimmed : `${trimmed}/v1`;
108
- }
109
-
110
- function normalizeOperationPath(pathname: string): string {
111
- // Remove leading/trailing slashes, lowercase
112
- return `/${pathname.toLowerCase().trim().replace(/^\/+|\/+$/g, '')}`;
113
- }
114
-
115
- export async function POST(request: ExpoRequest) {
116
- try {
117
- // Parse segments from URL
118
- const url = new URL(request.url);
119
- const pathname = url.pathname;
120
-
121
- // Extract segments after route prefix
122
- const segments = pathname
123
- .replace('/public-facing/api/quantum/v1', '')
124
- .split('/')
125
- .filter(Boolean);
126
-
127
- const operationPath = '/' + segments.join('/');
128
-
129
- // Validate disallowed paths
130
- for (const prefix of DISALLOWED_PREFIXES) {
131
- if (operationPath.startsWith(prefix)) {
132
- return new ExpoResponse(
133
- JSON.stringify({ error: 'Path not allowed' }),
134
- { status: 403, headers: { 'Content-Type': 'application/json' } }
135
- );
136
- }
137
- }
138
-
139
- // Forward to upstream
140
- const upstreamUrl = normalizeUpstreamBaseUrl() + operationPath;
141
- const upstreamRequest = new Request(upstreamUrl, {
142
- method: 'POST',
143
- headers: request.headers,
144
- body: await request.text(),
145
- });
146
-
147
- const response = await fetch(upstreamRequest);
148
- return new ExpoResponse(await response.text(), {
149
- status: response.status,
150
- headers: Object.fromEntries(response.headers),
151
- });
152
- } catch (error) {
153
- return new ExpoResponse(
154
- JSON.stringify({ error: 'Internal Server Error' }),
155
- { status: 500, headers: { 'Content-Type': 'application/json' } }
156
- );
157
- }
158
- }
159
- ```
160
-
161
- **From:** DJsPortfolio/src/app/public-facing/api/quantum/v1/[...segments]+api.ts (lines 1-50)
162
-
163
- ## Configuration
164
-
165
- ### Enable API Routes in app.json
166
-
167
- ```json
168
- {
169
- "expo": {
170
- "plugins": [
171
- [
172
- "expo-router",
173
- {
174
- "apiRoutes": true,
175
- "origin": false
176
- }
177
- ]
178
- ]
179
- }
180
- }
181
- ```
182
-
183
- ### Environment Variables for API Base URLs
184
-
185
- ```bash
186
- # .env or .env.production
187
- EXPO_PUBLIC_QUANTUM_API_BASE_URL=http://127.0.0.1:8000/v1
188
- QUANTUM_UPSTREAM_API_SECRET=your-secret-key
189
- ```
190
-
191
- ### TypeScript Setup
192
-
193
- ```typescript
194
- import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
195
-
196
- export type { ExpoRequest, ExpoResponse };
197
-
198
- // Type for allowed HTTP methods
199
- type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
200
- ```
201
-
202
- ## Best Practices
203
-
204
- ### ✅ DO
205
-
206
- 1. **Always validate CORS origin** before responding
207
- ```typescript
208
- const ALLOWED_ORIGINS = ['http://localhost:3000', 'https://app.example.com'];
209
-
210
- function validateOrigin(origin: string | null): boolean {
211
- return !!origin && ALLOWED_ORIGINS.includes(origin);
212
- }
213
- ```
214
-
215
- 2. **Normalize paths and trim trailing slashes**
216
- ```typescript
217
- function normalizePath(path: string): string {
218
- return path.replace(/\/+$/, '').toLowerCase();
219
- }
220
- ```
221
-
222
- 3. **Use explicit status codes**
223
- ```typescript
224
- // ✅ GOOD
225
- return new ExpoResponse(JSON.stringify(error), {
226
- status: 400,
227
- headers: { 'Content-Type': 'application/json' },
228
- });
229
- ```
230
-
231
- 4. **Handle disallowed paths explicitly**
232
- ```typescript
233
- const DISALLOWED_PREFIXES = ['/keys', '/credentials'];
234
-
235
- for (const prefix of DISALLOWED_PREFIXES) {
236
- if (operationPath.startsWith(prefix)) {
237
- return new ExpoResponse(
238
- JSON.stringify({ error: 'Forbidden' }),
239
- { status: 403 }
240
- );
241
- }
242
- }
243
- ```
244
-
245
- 5. **Support OPTIONS preflight requests**
246
- ```typescript
247
- if (request.method === 'OPTIONS') {
248
- return new ExpoResponse(null, {
249
- status: 204,
250
- headers: corsHeaders,
251
- });
252
- }
253
- ```
254
-
255
- ### ❌ DON'T
256
-
257
- 1. **Don't skip CORS validation**
258
- ```typescript
259
- // ❌ BAD - allows any origin
260
- headers: { 'Access-Control-Allow-Origin': '*' }
261
-
262
- // ✅ GOOD - restrict to known origins
263
- if (ALLOWED_ORIGINS.includes(origin)) {
264
- headers: { 'Access-Control-Allow-Origin': origin }
265
- }
266
- ```
267
-
268
- 2. **Don't expose sensitive environment variables**
269
- ```typescript
270
- // ❌ BAD
271
- headers: process.env.API_SECRET
272
-
273
- // ✅ GOOD - only use server-side secrets
274
- const secret = process.env.API_SECRET; // Used in headers, not exposed
275
- ```
276
-
277
- 3. **Don't pass unchecked path segments to upstream**
278
- ```typescript
279
- // ❌ BAD - could be exploited
280
- const upstreamUrl = baseUrl + segments;
281
-
282
- // ✅ GOOD - validate and normalize
283
- const validatedPath = normalizePath(segments);
284
- const upstreamUrl = baseUrl + validatedPath;
285
- ```
286
-
287
- 4. **Don't forget to handle errors**
288
- ```typescript
289
- // ❌ BAD - uncaught errors crash request
290
- const data = await request.json();
291
-
292
- // ✅ GOOD - catch and return error response
293
- try {
294
- const data = await request.json();
295
- } catch (error) {
296
- return new ExpoResponse(
297
- JSON.stringify({ error: 'Invalid JSON' }),
298
- { status: 400 }
299
- );
300
- }
301
- ```
302
-
303
- ## Related Patterns
304
-
305
- - [File-Based Routing](./file-based-routing.md) — Route file structure
306
- - [Error Handling](../api/error-handling.md) — Custom error classes
307
- - [CORS Configuration](../api/cors-configuration.md) — Origin validation
308
- - [Health Endpoints](../api/health-endpoints.md) — Health check patterns
309
-
310
- ---
311
-
312
- *Pattern extracted from production repositories: DJsPortfolio, PokePages*
313
- *Files: DJsPortfolio/src\app\api\quantum-backend\[...segments]+api.ts*
1
+ # API Routes with Catch-All Pattern
2
+
3
+ ## Description
4
+
5
+ Expo Router API routes use the `+api.ts` pattern to create server-side endpoints directly alongside client routes. The `[...segments]+api.ts` syntax creates catch-all routes that capture all path segments, enabling flexible API endpoint structures with full control over HTTP methods, CORS, and request handling.
6
+
7
+ ## When to Use
8
+
9
+ **Use API routes** for:
10
+ - ✅ Backend endpoints served from same domain as frontend
11
+ - ✅ Proxy servers that forward requests upstream
12
+ - ✅ Server-side validation and data transformation
13
+ - ✅ Protected endpoints with authentication
14
+ - ✅ Catch-all routes for dynamic path handling
15
+
16
+ ## Code Example
17
+
18
+ ### Basic API Route (Single Endpoint)
19
+
20
+ ```typescript
21
+ // File: app/api/quantum-backend/[...segments]+api.ts
22
+ import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
23
+
24
+ type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
25
+
26
+ const ALLOWED_ORIGINS = [
27
+ 'http://localhost:3000',
28
+ 'http://localhost:8081',
29
+ 'http://127.0.0.1:3000',
30
+ 'http://127.0.0.1:8081',
31
+ ];
32
+
33
+ function normalizeOrigin(request: ExpoRequest): string | null {
34
+ const origin = request.headers.get('origin');
35
+ if (!origin) return null;
36
+
37
+ return ALLOWED_ORIGINS.includes(origin) ? origin : null;
38
+ }
39
+
40
+ export async function GET(request: ExpoRequest) {
41
+ const allowedOrigin = normalizeOrigin(request);
42
+
43
+ if (request.method === 'OPTIONS') {
44
+ return new ExpoResponse(null, {
45
+ status: 204,
46
+ headers: allowedOrigin
47
+ ? { 'Access-Control-Allow-Origin': allowedOrigin }
48
+ : {},
49
+ });
50
+ }
51
+
52
+ // Handle GET request
53
+ return new ExpoResponse(JSON.stringify({ message: 'GET OK' }), {
54
+ status: 200,
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ ...(allowedOrigin && {
58
+ 'Access-Control-Allow-Origin': allowedOrigin,
59
+ }),
60
+ },
61
+ });
62
+ }
63
+
64
+ export async function POST(request: ExpoRequest) {
65
+ const allowedOrigin = normalizeOrigin(request);
66
+ const body = await request.json();
67
+
68
+ // Process request
69
+ const result = {
70
+ success: true,
71
+ data: body,
72
+ };
73
+
74
+ return new ExpoResponse(JSON.stringify(result), {
75
+ status: 200,
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ ...(allowedOrigin && {
79
+ 'Access-Control-Allow-Origin': allowedOrigin,
80
+ }),
81
+ },
82
+ });
83
+ }
84
+ ```
85
+
86
+ **From:** DJsPortfolio/src/app/api/quantum-backend/[...segments]+api.ts
87
+
88
+ ### Catch-All Route with Path Normalization
89
+
90
+ ```typescript
91
+ // File: app/public-facing/api/quantum/v1/[...segments]+api.ts
92
+ import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
93
+
94
+ const DISALLOWED_PREFIXES = ['/v1/keys', '/v1/ibm/profiles'];
95
+ const DEFAULT_UPSTREAM_BASE_URL = 'http://127.0.0.1:8000/v1';
96
+
97
+ type ProxyMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
98
+
99
+ function normalizeUpstreamBaseUrl(): string {
100
+ const raw = process.env.EXPO_PUBLIC_QUANTUM_API_BASE_URL?.trim();
101
+ const base = raw || DEFAULT_UPSTREAM_BASE_URL;
102
+
103
+ // Remove trailing slash
104
+ const trimmed = base.replace(/\/+$/, '');
105
+
106
+ // Ensure /v1 suffix
107
+ return trimmed.endsWith('/v1') ? trimmed : `${trimmed}/v1`;
108
+ }
109
+
110
+ function normalizeOperationPath(pathname: string): string {
111
+ // Remove leading/trailing slashes, lowercase
112
+ return `/${pathname.toLowerCase().trim().replace(/^\/+|\/+$/g, '')}`;
113
+ }
114
+
115
+ export async function POST(request: ExpoRequest) {
116
+ try {
117
+ // Parse segments from URL
118
+ const url = new URL(request.url);
119
+ const pathname = url.pathname;
120
+
121
+ // Extract segments after route prefix
122
+ const segments = pathname
123
+ .replace('/public-facing/api/quantum/v1', '')
124
+ .split('/')
125
+ .filter(Boolean);
126
+
127
+ const operationPath = '/' + segments.join('/');
128
+
129
+ // Validate disallowed paths
130
+ for (const prefix of DISALLOWED_PREFIXES) {
131
+ if (operationPath.startsWith(prefix)) {
132
+ return new ExpoResponse(
133
+ JSON.stringify({ error: 'Path not allowed' }),
134
+ { status: 403, headers: { 'Content-Type': 'application/json' } }
135
+ );
136
+ }
137
+ }
138
+
139
+ // Forward to upstream
140
+ const upstreamUrl = normalizeUpstreamBaseUrl() + operationPath;
141
+ const upstreamRequest = new Request(upstreamUrl, {
142
+ method: 'POST',
143
+ headers: request.headers,
144
+ body: await request.text(),
145
+ });
146
+
147
+ const response = await fetch(upstreamRequest);
148
+ return new ExpoResponse(await response.text(), {
149
+ status: response.status,
150
+ headers: Object.fromEntries(response.headers),
151
+ });
152
+ } catch (error) {
153
+ return new ExpoResponse(
154
+ JSON.stringify({ error: 'Internal Server Error' }),
155
+ { status: 500, headers: { 'Content-Type': 'application/json' } }
156
+ );
157
+ }
158
+ }
159
+ ```
160
+
161
+ **From:** DJsPortfolio/src/app/public-facing/api/quantum/v1/[...segments]+api.ts (lines 1-50)
162
+
163
+ ## Configuration
164
+
165
+ ### Enable API Routes in app.json
166
+
167
+ ```json
168
+ {
169
+ "expo": {
170
+ "plugins": [
171
+ [
172
+ "expo-router",
173
+ {
174
+ "apiRoutes": true,
175
+ "origin": false
176
+ }
177
+ ]
178
+ ]
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### Environment Variables for API Base URLs
184
+
185
+ ```bash
186
+ # .env or .env.production
187
+ EXPO_PUBLIC_QUANTUM_API_BASE_URL=http://127.0.0.1:8000/v1
188
+ QUANTUM_UPSTREAM_API_SECRET=your-secret-key
189
+ ```
190
+
191
+ ### TypeScript Setup
192
+
193
+ ```typescript
194
+ import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
195
+
196
+ export type { ExpoRequest, ExpoResponse };
197
+
198
+ // Type for allowed HTTP methods
199
+ type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
200
+ ```
201
+
202
+ ## Best Practices
203
+
204
+ ### ✅ DO
205
+
206
+ 1. **Always validate CORS origin** before responding
207
+ ```typescript
208
+ const ALLOWED_ORIGINS = ['http://localhost:3000', 'https://app.example.com'];
209
+
210
+ function validateOrigin(origin: string | null): boolean {
211
+ return !!origin && ALLOWED_ORIGINS.includes(origin);
212
+ }
213
+ ```
214
+
215
+ 2. **Normalize paths and trim trailing slashes**
216
+ ```typescript
217
+ function normalizePath(path: string): string {
218
+ return path.replace(/\/+$/, '').toLowerCase();
219
+ }
220
+ ```
221
+
222
+ 3. **Use explicit status codes**
223
+ ```typescript
224
+ // ✅ GOOD
225
+ return new ExpoResponse(JSON.stringify(error), {
226
+ status: 400,
227
+ headers: { 'Content-Type': 'application/json' },
228
+ });
229
+ ```
230
+
231
+ 4. **Handle disallowed paths explicitly**
232
+ ```typescript
233
+ const DISALLOWED_PREFIXES = ['/keys', '/credentials'];
234
+
235
+ for (const prefix of DISALLOWED_PREFIXES) {
236
+ if (operationPath.startsWith(prefix)) {
237
+ return new ExpoResponse(
238
+ JSON.stringify({ error: 'Forbidden' }),
239
+ { status: 403 }
240
+ );
241
+ }
242
+ }
243
+ ```
244
+
245
+ 5. **Support OPTIONS preflight requests**
246
+ ```typescript
247
+ if (request.method === 'OPTIONS') {
248
+ return new ExpoResponse(null, {
249
+ status: 204,
250
+ headers: corsHeaders,
251
+ });
252
+ }
253
+ ```
254
+
255
+ ### ❌ DON'T
256
+
257
+ 1. **Don't skip CORS validation**
258
+ ```typescript
259
+ // ❌ BAD - allows any origin
260
+ headers: { 'Access-Control-Allow-Origin': '*' }
261
+
262
+ // ✅ GOOD - restrict to known origins
263
+ if (ALLOWED_ORIGINS.includes(origin)) {
264
+ headers: { 'Access-Control-Allow-Origin': origin }
265
+ }
266
+ ```
267
+
268
+ 2. **Don't expose sensitive environment variables**
269
+ ```typescript
270
+ // ❌ BAD
271
+ headers: process.env.API_SECRET
272
+
273
+ // ✅ GOOD - only use server-side secrets
274
+ const secret = process.env.API_SECRET; // Used in headers, not exposed
275
+ ```
276
+
277
+ 3. **Don't pass unchecked path segments to upstream**
278
+ ```typescript
279
+ // ❌ BAD - could be exploited
280
+ const upstreamUrl = baseUrl + segments;
281
+
282
+ // ✅ GOOD - validate and normalize
283
+ const validatedPath = normalizePath(segments);
284
+ const upstreamUrl = baseUrl + validatedPath;
285
+ ```
286
+
287
+ 4. **Don't forget to handle errors**
288
+ ```typescript
289
+ // ❌ BAD - uncaught errors crash request
290
+ const data = await request.json();
291
+
292
+ // ✅ GOOD - catch and return error response
293
+ try {
294
+ const data = await request.json();
295
+ } catch (error) {
296
+ return new ExpoResponse(
297
+ JSON.stringify({ error: 'Invalid JSON' }),
298
+ { status: 400 }
299
+ );
300
+ }
301
+ ```
302
+
303
+ ## Related Patterns
304
+
305
+ - [File-Based Routing](./file-based-routing.md) — Route file structure
306
+ - [Error Handling](../api/error-handling.md) — Custom error classes
307
+ - [CORS Configuration](../api/cors-configuration.md) — Origin validation
308
+ - [Health Endpoints](../api/health-endpoints.md) — Health check patterns
309
+
310
+ ---
311
+
312
+ *Pattern extracted from production repositories: DJsPortfolio, PokePages*
313
+ *Files: DJsPortfolio/src\app\api\quantum-backend\[...segments]+api.ts*
314
314
  *Lines 1-40 of catch-all route pattern with path normalization*