@odvi/create-dtt-framework 0.1.3 → 0.1.6

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 (111) hide show
  1. package/dist/commands/create.d.ts.map +1 -1
  2. package/dist/commands/create.js +16 -13
  3. package/dist/commands/create.js.map +1 -1
  4. package/package.json +3 -2
  5. package/template/.env.example +106 -0
  6. package/template/components.json +22 -0
  7. package/template/docs/framework/01-overview.md +289 -0
  8. package/template/docs/framework/02-techstack.md +503 -0
  9. package/template/docs/framework/api-layer.md +681 -0
  10. package/template/docs/framework/clerk-authentication.md +649 -0
  11. package/template/docs/framework/cli-installation.md +564 -0
  12. package/template/docs/framework/deployment/ci-cd.md +907 -0
  13. package/template/docs/framework/deployment/digitalocean.md +991 -0
  14. package/template/docs/framework/deployment/domain-setup.md +972 -0
  15. package/template/docs/framework/deployment/environment-variables.md +862 -0
  16. package/template/docs/framework/deployment/monitoring.md +927 -0
  17. package/template/docs/framework/deployment/production-checklist.md +649 -0
  18. package/template/docs/framework/deployment/vercel.md +791 -0
  19. package/template/docs/framework/environment-variables.md +646 -0
  20. package/template/docs/framework/health-check-system.md +583 -0
  21. package/template/docs/framework/implementation.md +559 -0
  22. package/template/docs/framework/snowflake-integration.md +594 -0
  23. package/template/docs/framework/state-management.md +615 -0
  24. package/template/docs/framework/supabase-integration.md +582 -0
  25. package/template/docs/framework/testing-guide.md +544 -0
  26. package/template/docs/framework/what-did-i-miss.md +526 -0
  27. package/template/drizzle.config.ts +11 -0
  28. package/template/next.config.js +21 -0
  29. package/template/postcss.config.js +5 -0
  30. package/template/prettier.config.js +4 -0
  31. package/template/public/favicon.ico +0 -0
  32. package/template/src/app/(auth)/layout.tsx +4 -0
  33. package/template/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +10 -0
  34. package/template/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx +10 -0
  35. package/template/src/app/(dashboard)/dashboard/page.tsx +8 -0
  36. package/template/src/app/(dashboard)/health/page.tsx +16 -0
  37. package/template/src/app/(dashboard)/layout.tsx +17 -0
  38. package/template/src/app/api/[[...route]]/route.ts +11 -0
  39. package/template/src/app/api/debug-files/route.ts +33 -0
  40. package/template/src/app/api/webhooks/clerk/route.ts +112 -0
  41. package/template/src/app/layout.tsx +28 -0
  42. package/template/src/app/page.tsx +12 -0
  43. package/template/src/app/providers.tsx +20 -0
  44. package/template/src/components/layouts/navbar.tsx +14 -0
  45. package/template/src/components/shared/loading-spinner.tsx +6 -0
  46. package/template/src/components/ui/badge.tsx +46 -0
  47. package/template/src/components/ui/button.tsx +62 -0
  48. package/template/src/components/ui/card.tsx +92 -0
  49. package/template/src/components/ui/collapsible.tsx +33 -0
  50. package/template/src/components/ui/scroll-area.tsx +58 -0
  51. package/template/src/components/ui/sheet.tsx +139 -0
  52. package/template/src/config/__tests__/env.test.ts +164 -0
  53. package/template/src/config/__tests__/site.test.ts +46 -0
  54. package/template/src/config/env.ts +36 -0
  55. package/template/src/config/site.ts +10 -0
  56. package/template/src/env.js +44 -0
  57. package/template/src/features/__tests__/health-check-config.test.ts +142 -0
  58. package/template/src/features/__tests__/health-check-types.test.ts +201 -0
  59. package/template/src/features/documentation/components/doc-sidebar.tsx +109 -0
  60. package/template/src/features/documentation/components/doc-viewer.tsx +70 -0
  61. package/template/src/features/documentation/index.tsx +92 -0
  62. package/template/src/features/documentation/utils/doc-loader.ts +177 -0
  63. package/template/src/features/health-check/components/health-dashboard.tsx +374 -0
  64. package/template/src/features/health-check/config.ts +71 -0
  65. package/template/src/features/health-check/index.ts +4 -0
  66. package/template/src/features/health-check/stores/health-store.ts +14 -0
  67. package/template/src/features/health-check/types.ts +18 -0
  68. package/template/src/hooks/__tests__/use-debounce.test.tsx +28 -0
  69. package/template/src/hooks/queries/use-health-checks.ts +16 -0
  70. package/template/src/hooks/utils/use-debounce.ts +20 -0
  71. package/template/src/lib/__tests__/utils.test.ts +52 -0
  72. package/template/src/lib/__tests__/validators.test.ts +114 -0
  73. package/template/src/lib/nextbank/client.ts +67 -0
  74. package/template/src/lib/snowflake/client.ts +102 -0
  75. package/template/src/lib/supabase/admin.ts +7 -0
  76. package/template/src/lib/supabase/client.ts +7 -0
  77. package/template/src/lib/supabase/server.ts +23 -0
  78. package/template/src/lib/utils.ts +6 -0
  79. package/template/src/lib/validators.ts +9 -0
  80. package/template/src/middleware.ts +22 -0
  81. package/template/src/server/api/index.ts +22 -0
  82. package/template/src/server/api/middleware/auth.ts +19 -0
  83. package/template/src/server/api/middleware/logger.ts +4 -0
  84. package/template/src/server/api/routes/health/clerk.ts +214 -0
  85. package/template/src/server/api/routes/health/database.ts +141 -0
  86. package/template/src/server/api/routes/health/edge-functions.ts +107 -0
  87. package/template/src/server/api/routes/health/framework.ts +48 -0
  88. package/template/src/server/api/routes/health/index.ts +102 -0
  89. package/template/src/server/api/routes/health/nextbank.ts +46 -0
  90. package/template/src/server/api/routes/health/snowflake.ts +83 -0
  91. package/template/src/server/api/routes/health/storage.ts +177 -0
  92. package/template/src/server/api/routes/users.ts +79 -0
  93. package/template/src/server/db/index.ts +17 -0
  94. package/template/src/server/db/queries/users.ts +8 -0
  95. package/template/src/server/db/schema/__tests__/health-checks.test.ts +31 -0
  96. package/template/src/server/db/schema/__tests__/users.test.ts +46 -0
  97. package/template/src/server/db/schema/health-checks.ts +11 -0
  98. package/template/src/server/db/schema/index.ts +2 -0
  99. package/template/src/server/db/schema/users.ts +16 -0
  100. package/template/src/server/db/schema.ts +1 -0
  101. package/template/src/stores/__tests__/ui-store.test.ts +87 -0
  102. package/template/src/stores/ui-store.ts +14 -0
  103. package/template/src/styles/globals.css +129 -0
  104. package/template/src/test/mocks/clerk.ts +35 -0
  105. package/template/src/test/mocks/snowflake.ts +28 -0
  106. package/template/src/test/mocks/supabase.ts +37 -0
  107. package/template/src/test/setup.ts +69 -0
  108. package/template/src/test/utils/test-helpers.ts +158 -0
  109. package/template/src/types/index.ts +14 -0
  110. package/template/tsconfig.json +43 -0
  111. package/template/vitest.config.ts +44 -0
@@ -0,0 +1,681 @@
1
+ # DTT Framework - API Layer
2
+
3
+ ## Overview
4
+
5
+ The DTT Framework uses [Hono](https://hono.dev/) as the API layer. Hono is a lightweight, fast web framework that provides an excellent developer experience with full TypeScript support.
6
+
7
+ ### Why Hono?
8
+
9
+ - **Lightweight**: Small bundle size (~14KB)
10
+ - **Fast**: Built on Web Standards, optimized for performance
11
+ - **Type-Safe**: Full TypeScript support with inferred types
12
+ - **Middleware Support**: Composable middleware system
13
+ - **Flexible**: Works with multiple runtimes (Node.js, Edge, Deno)
14
+ - **Easy to Learn**: Simple, intuitive API
15
+
16
+ ---
17
+
18
+ ## Hono Framework Setup
19
+
20
+ ### 1. Install Hono
21
+
22
+ ```bash
23
+ pnpm add hono
24
+ ```
25
+
26
+ ### 2. Create Hono App
27
+
28
+ Create [`src/server/api/index.ts`](../../src/server/api/index.ts):
29
+
30
+ ```typescript
31
+ import { Hono } from 'hono'
32
+ import { cors } from 'hono/cors'
33
+ import { logger } from 'hono/logger'
34
+ import { authMiddleware } from './middleware/auth'
35
+ import { healthRoutes } from './routes/health'
36
+ import { usersRoutes } from './routes/users'
37
+
38
+ const app = new Hono().basePath('/api')
39
+
40
+ // Global middleware
41
+ app.use('*', logger())
42
+ app.use('*', cors())
43
+
44
+ // Public routes
45
+ app.get('/ping', (c) => c.json({ status: 'ok', timestamp: new Date().toISOString() }))
46
+
47
+ // Protected routes - require authentication
48
+ app.use('*', authMiddleware)
49
+ app.route('/health', healthRoutes)
50
+ app.route('/users', usersRoutes)
51
+
52
+ export { app }
53
+ export type AppType = typeof app
54
+ ```
55
+
56
+ ### 3. Mount Hono in Next.js
57
+
58
+ Create [`src/app/api/[[...route]]/route.ts`](../../src/app/api/[[...route]]/route.ts):
59
+
60
+ ```typescript
61
+ import { handle } from 'hono/vercel'
62
+ import { app } from '@/server/api'
63
+
64
+ export const runtime = 'nodejs'
65
+
66
+ export const GET = handle(app)
67
+ export const POST = handle(app)
68
+ export const PUT = handle(app)
69
+ export const DELETE = handle(app)
70
+ export const PATCH = handle(app)
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Route Organization
76
+
77
+ ### Directory Structure
78
+
79
+ ```
80
+ src/server/api/
81
+ ├── index.ts # Hono app instance
82
+ ├── middleware/ # Middleware functions
83
+ │ ├── auth.ts # Authentication middleware
84
+ │ └── logger.ts # Logger middleware
85
+ └── routes/ # Route definitions
86
+ ├── health/ # Health check routes
87
+ │ ├── index.ts # Health route aggregation
88
+ │ ├── clerk.ts # Clerk health checks
89
+ │ ├── database.ts # Database health checks
90
+ │ ├── storage.ts # Storage health checks
91
+ │ ├── edge-functions.ts # Edge function checks
92
+ │ ├── snowflake.ts # Snowflake health checks
93
+ │ └── nextbank.ts # NextBank health checks
94
+ └── users.ts # User routes
95
+ ```
96
+
97
+ ### Route Aggregation
98
+
99
+ Health routes are aggregated in [`src/server/api/routes/health/index.ts`](../../src/server/api/routes/health/index.ts):
100
+
101
+ ```typescript
102
+ import { Hono } from 'hono'
103
+ import { clerkHealthRoutes } from './clerk'
104
+ import { databaseHealthRoutes } from './database'
105
+ import { storageHealthRoutes } from './storage'
106
+ import { edgeFunctionsHealthRoutes } from './edge-functions'
107
+ import { snowflakeHealthRoutes } from './snowflake'
108
+ import { nextbankHealthRoutes } from './nextbank'
109
+
110
+ export const healthRoutes = new Hono()
111
+
112
+ healthRoutes.route('/clerk', clerkHealthRoutes)
113
+ healthRoutes.route('/database', databaseHealthRoutes)
114
+ healthRoutes.route('/storage', storageHealthRoutes)
115
+ healthRoutes.route('/edge', edgeFunctionsHealthRoutes)
116
+ healthRoutes.route('/snowflake', snowflakeHealthRoutes)
117
+ healthRoutes.route('/nextbank', nextbankHealthRoutes)
118
+ ```
119
+
120
+ ### Creating New Routes
121
+
122
+ **Step 1: Create route file**
123
+
124
+ ```typescript
125
+ // src/server/api/routes/products.ts
126
+ import { Hono } from 'hono'
127
+ import { db } from '@/server/db'
128
+
129
+ export const productsRoutes = new Hono()
130
+
131
+ productsRoutes.get('/', async (c) => {
132
+ const products = await db.query.products.findMany()
133
+ return c.json({ products })
134
+ })
135
+
136
+ productsRoutes.get('/:id', async (c) => {
137
+ const id = c.req.param('id')
138
+ const product = await db.query.products.findFirst({
139
+ where: eq(products.id, id),
140
+ })
141
+
142
+ if (!product) {
143
+ return c.json({ error: 'Product not found' }, 404)
144
+ }
145
+
146
+ return c.json({ product })
147
+ })
148
+ ```
149
+
150
+ **Step 2: Register route in main app**
151
+
152
+ ```typescript
153
+ // src/server/api/index.ts
154
+ import { productsRoutes } from './routes/products'
155
+
156
+ // Add to protected routes
157
+ app.route('/products', productsRoutes)
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Middleware Usage
163
+
164
+ ### Available Middleware
165
+
166
+ #### Logger Middleware
167
+
168
+ Located at [`src/server/api/middleware/logger.ts`](../../src/server/api/middleware/logger.ts):
169
+
170
+ ```typescript
171
+ import { logger } from 'hono/logger'
172
+
173
+ export { logger }
174
+ ```
175
+
176
+ The logger middleware logs all incoming requests to the console:
177
+
178
+ ```
179
+ GET /api/ping 200 5ms
180
+ POST /api/health/database/write 200 123ms
181
+ ```
182
+
183
+ #### Auth Middleware
184
+
185
+ Located at [`src/server/api/middleware/auth.ts`](../../src/server/api/middleware/auth.ts):
186
+
187
+ ```typescript
188
+ import { createMiddleware } from 'hono/factory'
189
+ import { getAuth } from '@clerk/nextjs/server'
190
+ import type { NextRequest } from 'next/server'
191
+
192
+ export const authMiddleware = createMiddleware(async (c, next) => {
193
+ const request = c.req.raw as NextRequest
194
+ const auth = getAuth(request)
195
+
196
+ if (!auth.userId) {
197
+ return c.json({ error: 'Unauthorized' }, 401)
198
+ }
199
+
200
+ c.set('auth', auth)
201
+ c.set('userId', auth.userId)
202
+ c.set('orgId', auth.orgId)
203
+
204
+ await next()
205
+ })
206
+ ```
207
+
208
+ ### Using Middleware
209
+
210
+ **Global middleware (applies to all routes):**
211
+
212
+ ```typescript
213
+ import { logger } from 'hono/logger'
214
+ import { cors } from 'hono/cors'
215
+
216
+ const app = new Hono()
217
+
218
+ app.use('*', logger())
219
+ app.use('*', cors())
220
+ ```
221
+
222
+ **Route-specific middleware:**
223
+
224
+ ```typescript
225
+ const protectedRoutes = new Hono()
226
+
227
+ protectedRoutes.use('*', authMiddleware)
228
+ protectedRoutes.get('/data', handler)
229
+ ```
230
+
231
+ **Middleware on specific routes:**
232
+
233
+ ```typescript
234
+ app.get('/public', handler)
235
+ app.get('/protected', authMiddleware, handler)
236
+ ```
237
+
238
+ ### Creating Custom Middleware
239
+
240
+ ```typescript
241
+ import { createMiddleware } from 'hono/factory'
242
+
243
+ export const timingMiddleware = createMiddleware(async (c, next) => {
244
+ const start = Date.now()
245
+ await next()
246
+ const duration = Date.now() - start
247
+ c.header('X-Response-Time', `${duration}ms`)
248
+ })
249
+ ```
250
+
251
+ ### Middleware Order
252
+
253
+ Middleware is executed in the order they are defined:
254
+
255
+ ```typescript
256
+ app.use('*', middleware1) // Executed first
257
+ app.use('*', middleware2) // Executed second
258
+ app.use('*', middleware3) // Executed third
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Authentication Middleware
264
+
265
+ ### How It Works
266
+
267
+ The auth middleware:
268
+
269
+ 1. Extracts the request from Hono context
270
+ 2. Calls Clerk's `getAuth()` to verify session
271
+ 3. Returns 401 if no user is authenticated
272
+ 4. Sets auth data in context for downstream handlers
273
+
274
+ ### Accessing Auth Data
275
+
276
+ **In route handlers:**
277
+
278
+ ```typescript
279
+ app.get('/protected', authMiddleware, async (c) => {
280
+ const userId = c.get('userId')
281
+ const orgId = c.get('orgId')
282
+ const auth = c.get('auth')
283
+
284
+ return c.json({ userId, orgId })
285
+ })
286
+ ```
287
+
288
+ ### Middleware Context Types
289
+
290
+ ```typescript
291
+ import type { Context } from 'hono'
292
+
293
+ // Extend Hono context with your custom types
294
+ type Env = {
295
+ Variables: {
296
+ userId: string
297
+ orgId: string | null
298
+ auth: any
299
+ }
300
+ }
301
+
302
+ const app = new Hono<Env>()
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Request/Response Patterns
308
+
309
+ ### Request Handling
310
+
311
+ **Getting query parameters:**
312
+
313
+ ```typescript
314
+ app.get('/search', async (c) => {
315
+ const query = c.req.query('q')
316
+ const page = c.req.query('page') || '1'
317
+ const limit = c.req.query('limit') || '10'
318
+
319
+ return c.json({ query, page, limit })
320
+ })
321
+ ```
322
+
323
+ **Getting path parameters:**
324
+
325
+ ```typescript
326
+ app.get('/users/:id', async (c) => {
327
+ const id = c.req.param('id')
328
+ return c.json({ userId: id })
329
+ })
330
+ ```
331
+
332
+ **Getting request body:**
333
+
334
+ ```typescript
335
+ app.post('/users', async (c) => {
336
+ const body = await c.req.json()
337
+ const { name, email } = body
338
+
339
+ return c.json({ name, email })
340
+ })
341
+ ```
342
+
343
+ **Getting headers:**
344
+
345
+ ```typescript
346
+ app.get('/data', async (c) => {
347
+ const authHeader = c.req.header('authorization')
348
+ const contentType = c.req.header('content-type')
349
+
350
+ return c.json({ authHeader, contentType })
351
+ })
352
+ ```
353
+
354
+ ### Response Handling
355
+
356
+ **JSON response:**
357
+
358
+ ```typescript
359
+ app.get('/data', (c) => {
360
+ return c.json({ message: 'Hello, World!' })
361
+ })
362
+ ```
363
+
364
+ **Setting status code:**
365
+
366
+ ```typescript
367
+ app.get('/not-found', (c) => {
368
+ return c.json({ error: 'Not found' }, 404)
369
+ })
370
+ ```
371
+
372
+ **Setting headers:**
373
+
374
+ ```typescript
375
+ app.get('/data', (c) => {
376
+ return c.json({ data: '...' }, 200, {
377
+ 'X-Custom-Header': 'value',
378
+ })
379
+ })
380
+ ```
381
+
382
+ **Text response:**
383
+
384
+ ```typescript
385
+ app.get('/text', (c) => {
386
+ return c.text('Plain text response')
387
+ })
388
+ ```
389
+
390
+ **File response:**
391
+
392
+ ```typescript
393
+ app.get('/download', (c) => {
394
+ return c.file('/path/to/file.pdf')
395
+ })
396
+ ```
397
+
398
+ ### Error Handling
399
+
400
+ **Try-catch pattern:**
401
+
402
+ ```typescript
403
+ app.get('/data', async (c) => {
404
+ try {
405
+ const data = await fetchData()
406
+ return c.json({ data })
407
+ } catch (error) {
408
+ return c.json(
409
+ { error: error instanceof Error ? error.message : 'Unknown error' },
410
+ 500
411
+ )
412
+ }
413
+ })
414
+ ```
415
+
416
+ **Global error handler:**
417
+
418
+ ```typescript
419
+ app.onError((err, c) => {
420
+ console.error('Error:', err)
421
+ return c.json(
422
+ { error: 'Internal server error' },
423
+ 500
424
+ )
425
+ })
426
+ ```
427
+
428
+ **Not found handler:**
429
+
430
+ ```typescript
431
+ app.notFound((c) => {
432
+ return c.json(
433
+ { error: 'Not found' },
434
+ 404
435
+ )
436
+ })
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Route Examples
442
+
443
+ ### GET Request
444
+
445
+ ```typescript
446
+ app.get('/users', async (c) => {
447
+ const users = await db.select().from(usersTable)
448
+ return c.json({ users })
449
+ })
450
+ ```
451
+
452
+ ### POST Request
453
+
454
+ ```typescript
455
+ app.post('/users', async (c) => {
456
+ const body = await c.req.json()
457
+ const { email, name } = body
458
+
459
+ const user = await db.insert(usersTable).values({
460
+ email,
461
+ name,
462
+ }).returning()
463
+
464
+ return c.json({ user: user[0] }, 201)
465
+ })
466
+ ```
467
+
468
+ ### PUT Request
469
+
470
+ ```typescript
471
+ app.put('/users/:id', async (c) => {
472
+ const id = c.req.param('id')
473
+ const body = await c.req.json()
474
+ const { name } = body
475
+
476
+ const user = await db.update(usersTable)
477
+ .set({ name })
478
+ .where(eq(usersTable.id, id))
479
+ .returning()
480
+
481
+ return c.json({ user: user[0] })
482
+ })
483
+ ```
484
+
485
+ ### DELETE Request
486
+
487
+ ```typescript
488
+ app.delete('/users/:id', async (c) => {
489
+ const id = c.req.param('id')
490
+
491
+ await db.delete(usersTable).where(eq(usersTable.id, id))
492
+
493
+ return c.json({ message: 'User deleted' })
494
+ })
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Type Safety
500
+
501
+ ### Inferring Request Types
502
+
503
+ ```typescript
504
+ import type { z } from 'zod'
505
+
506
+ const userSchema = z.object({
507
+ name: z.string(),
508
+ email: z.string().email(),
509
+ })
510
+
511
+ app.post('/users', async (c) => {
512
+ const body = await c.req.json()
513
+ const user = userSchema.parse(body)
514
+
515
+ // user is now typed as { name: string; email: string }
516
+ return c.json({ user })
517
+ })
518
+ ```
519
+
520
+ ### Inferring Response Types
521
+
522
+ ```typescript
523
+ const app = new Hono()
524
+
525
+ app.get('/data', (c) => {
526
+ return c.json<{ message: string }>({ message: 'Hello' })
527
+ })
528
+
529
+ // Type-safe client
530
+ type AppType = typeof app
531
+ ```
532
+
533
+ ---
534
+
535
+ ## Health Check Routes
536
+
537
+ ### Available Health Check Endpoints
538
+
539
+ | Service | Endpoint | Method | Description |
540
+ |---------|----------|--------|-------------|
541
+ | **Clerk** | `/api/health/clerk/user` | GET | Get current user |
542
+ | **Clerk** | `/api/health/clerk/org` | GET | Get org membership |
543
+ | **Clerk** | `/api/health/clerk/members` | GET | List org members |
544
+ | **Database** | `/api/health/database/write` | POST | Write test row |
545
+ | **Database** | `/api/health/database/read` | GET | Read test row |
546
+ | **Database** | `/api/health/database/delete` | DELETE | Delete test row |
547
+ | **Storage** | `/api/health/storage/upload` | POST | Upload test file |
548
+ | **Storage** | `/api/health/storage/download` | GET | Download test file |
549
+ | **Storage** | `/api/health/storage/delete` | DELETE | Delete test file |
550
+ | **Edge Functions** | `/api/health/edge/ping` | GET | Ping edge function |
551
+ | **Edge Functions** | `/api/health/edge/auth` | GET | Test auth header |
552
+ | **Snowflake** | `/api/health/snowflake/connect` | GET | Test connection |
553
+ | **Snowflake** | `/api/health/snowflake/query` | GET | Execute test query |
554
+ | **NextBank** | `/api/health/nextbank/ping` | GET | Ping API |
555
+ | **NextBank** | `/api/health/nextbank/auth` | GET | Test authentication |
556
+ | **All** | `/api/health/all` | GET | Run all checks |
557
+
558
+ ---
559
+
560
+ ## Best Practices
561
+
562
+ ### 1. Use TypeScript
563
+
564
+ Always leverage TypeScript for type safety:
565
+
566
+ ```typescript
567
+ // ✅ Good - Type-safe
568
+ app.get('/users/:id', async (c) => {
569
+ const id = c.req.param('id')
570
+ // TypeScript knows id is a string
571
+ })
572
+
573
+ // ❌ Bad - No type safety
574
+ app.get('/users/:id', async (c) => {
575
+ const id = c.req.param('id')
576
+ // Could be undefined
577
+ })
578
+ ```
579
+
580
+ ### 2. Validate Input
581
+
582
+ Always validate request input:
583
+
584
+ ```typescript
585
+ import { z } from 'zod'
586
+
587
+ const createUserSchema = z.object({
588
+ email: z.string().email(),
589
+ name: z.string().min(2),
590
+ })
591
+
592
+ app.post('/users', async (c) => {
593
+ const body = await c.req.json()
594
+ const validated = createUserSchema.parse(body)
595
+ // validated is now type-safe
596
+ })
597
+ ```
598
+
599
+ ### 3. Handle Errors Gracefully
600
+
601
+ Always handle errors properly:
602
+
603
+ ```typescript
604
+ app.get('/data', async (c) => {
605
+ try {
606
+ const data = await fetchData()
607
+ return c.json({ data })
608
+ } catch (error) {
609
+ console.error('Error fetching data:', error)
610
+ return c.json(
611
+ { error: 'Failed to fetch data' },
612
+ 500
613
+ )
614
+ }
615
+ })
616
+ ```
617
+
618
+ ### 4. Use Middleware for Cross-Cutting Concerns
619
+
620
+ Use middleware for authentication, logging, etc.:
621
+
622
+ ```typescript
623
+ app.use('*', authMiddleware)
624
+ app.use('*', logger())
625
+ app.use('*', timingMiddleware())
626
+ ```
627
+
628
+ ### 5. Keep Routes Focused
629
+
630
+ Keep each route focused on a single responsibility:
631
+
632
+ ```typescript
633
+ // ✅ Good - Focused route
634
+ app.get('/users/:id', async (c) => {
635
+ const user = await getUserById(c.req.param('id'))
636
+ return c.json({ user })
637
+ })
638
+
639
+ // ❌ Bad - Doing too much
640
+ app.get('/users/:id', async (c) => {
641
+ const user = await getUserById(c.req.param('id'))
642
+ const posts = await getUserPosts(user.id)
643
+ const comments = await getUserComments(user.id)
644
+ const analytics = await getUserAnalytics(user.id)
645
+ return c.json({ user, posts, comments, analytics })
646
+ })
647
+ ```
648
+
649
+ ---
650
+
651
+ ## Troubleshooting
652
+
653
+ ### Common Issues
654
+
655
+ **Issue: Routes not found**
656
+
657
+ - Verify route is registered in main app
658
+ - Check base path configuration
659
+ - Ensure route file is imported
660
+
661
+ **Issue: Auth middleware not working**
662
+
663
+ - Verify Clerk is configured
664
+ - Check if auth token is being sent
665
+ - Ensure middleware is applied before route
666
+
667
+ **Issue: CORS errors**
668
+
669
+ - Verify CORS middleware is applied
670
+ - Check allowed origins configuration
671
+ - Ensure preflight requests are handled
672
+
673
+ ---
674
+
675
+ ## Related Documentation
676
+
677
+ - [Clerk Authentication](./clerk-authentication.md) - Authentication setup
678
+ - [Supabase Integration](./supabase-integration.md) - Database queries
679
+ - [Snowflake Integration](./snowflake-integration.md) - Snowflake queries
680
+ - [Health Check System](./health-check-system.md) - Health check endpoints
681
+ - [State Management](./state-management.md) - Data fetching patterns