@mr.dj2u/knowledge 0.1.6 → 0.1.8

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 (74) 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/animation-performance.md +31 -1
  6. package/dist/content/guides/post-create-onboarding.md +140 -140
  7. package/dist/content/patterns/animation/animation-motion-selection.md +155 -0
  8. package/dist/content/patterns/api/api-routes.md +313 -313
  9. package/dist/content/patterns/api/error-handling.md +310 -310
  10. package/dist/content/patterns/database/drizzle-schema.md +279 -279
  11. package/dist/content/patterns/database/migrations.md +364 -364
  12. package/dist/content/patterns/database/query-organization.md +536 -536
  13. package/dist/content/patterns/database/relations.md +449 -449
  14. package/dist/content/patterns/deployment/build-configuration.md +440 -440
  15. package/dist/content/patterns/deployment/ci-cd-patterns.md +447 -447
  16. package/dist/content/patterns/deployment/environment-config.md +379 -379
  17. package/dist/content/patterns/deployment/hosting-setup.md +424 -424
  18. package/dist/content/patterns/project/configuration-patterns.md +459 -459
  19. package/dist/content/patterns/project/documentation-org.md +506 -506
  20. package/dist/content/patterns/project/folder-structure.md +397 -397
  21. package/dist/content/patterns/project/library-exports.md +464 -464
  22. package/dist/content/patterns/project/monorepo-structure.md +500 -500
  23. package/dist/content/patterns/routing/dynamic-routes.md +220 -220
  24. package/dist/content/patterns/routing/file-based-routing.md +185 -185
  25. package/dist/content/patterns/routing/route-groups.md +428 -428
  26. package/dist/content/patterns/state/persistence-middleware.md +520 -520
  27. package/dist/content/patterns/state/selector-hooks.md +537 -537
  28. package/dist/content/patterns/state/store-organization.md +538 -538
  29. package/dist/content/patterns/state/zustand-patterns.md +347 -347
  30. package/dist/content/patterns/styling/component-styling.md +467 -467
  31. package/dist/content/patterns/styling/responsive-patterns.md +397 -397
  32. package/dist/content/patterns/styling/theme-configuration.md +425 -425
  33. package/dist/content/patterns/styling/uniwind-setup.md +411 -411
  34. package/dist/content/prompts/continue-development.md +35 -35
  35. package/dist/content/prompts/fix-seo.md +29 -29
  36. package/dist/content/prompts/onboard-new-expo-app.md +11 -11
  37. package/dist/content/prompts/prepare-deploy.md +29 -29
  38. package/dist/content/prompts/project-research-plan.md +29 -29
  39. package/dist/content/prompts/push-merge-loop.md +25 -25
  40. package/dist/content/prompts/review-expo-project.md +29 -29
  41. package/dist/content/prompts/review-motion.md +58 -0
  42. package/dist/content/prompts/run-doctor.md +38 -38
  43. package/dist/content/prompts/wrap-up.md +67 -67
  44. package/dist/content/reference/create-expo-stack-uniwind.md +29 -29
  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 +3 -0
  48. package/dist/content/skills/animation-motion.md +68 -0
  49. package/dist/content/skills/api-routes.md +33 -33
  50. package/dist/content/skills/continue-development.md +32 -32
  51. package/dist/content/skills/debugging.md +32 -32
  52. package/dist/content/skills/deployment.md +32 -32
  53. package/dist/content/skills/dev-server-management.md +32 -32
  54. package/dist/content/skills/env-vars.md +32 -32
  55. package/dist/content/skills/expo-router-architecture.md +33 -33
  56. package/dist/content/skills/expo-ssr-safety.md +32 -32
  57. package/dist/content/skills/plugin-creation.md +41 -41
  58. package/dist/content/skills/production-server-patterns.md +31 -31
  59. package/dist/content/skills/project-onboarding.md +31 -31
  60. package/dist/content/skills/research-plan-intake.md +32 -32
  61. package/dist/content/skills/seo-metadata.md +31 -31
  62. package/dist/content/skills/super-stack-startup.md +34 -34
  63. package/dist/content/skills/uniwind-theming.md +32 -32
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +27 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/patterns/index.d.ts +9 -1
  68. package/dist/patterns/index.d.ts.map +1 -1
  69. package/dist/patterns/index.js +17 -0
  70. package/dist/patterns/index.js.map +1 -1
  71. package/dist/prompts/index.d.ts.map +1 -1
  72. package/dist/prompts/index.js +17 -0
  73. package/dist/prompts/index.js.map +1 -1
  74. package/package.json +6 -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*