@kood/claude-code 0.1.6 → 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/index.js +109 -216
- package/package.json +8 -2
- package/templates/hono/CLAUDE.md +59 -328
- package/templates/hono/docs/architecture/architecture.md +93 -747
- package/templates/hono/docs/deployment/cloudflare.md +59 -513
- package/templates/hono/docs/deployment/docker.md +41 -356
- package/templates/hono/docs/deployment/index.md +54 -190
- package/templates/hono/docs/deployment/railway.md +36 -306
- package/templates/hono/docs/deployment/vercel.md +49 -434
- package/templates/hono/docs/library/ai-sdk/index.md +53 -290
- package/templates/hono/docs/library/ai-sdk/openrouter.md +19 -387
- package/templates/hono/docs/library/ai-sdk/providers.md +28 -394
- package/templates/hono/docs/library/ai-sdk/streaming.md +52 -353
- package/templates/hono/docs/library/ai-sdk/structured-output.md +63 -395
- package/templates/hono/docs/library/ai-sdk/tools.md +62 -431
- package/templates/hono/docs/library/hono/env-setup.md +24 -313
- package/templates/hono/docs/library/hono/error-handling.md +34 -295
- package/templates/hono/docs/library/hono/index.md +29 -121
- package/templates/hono/docs/library/hono/middleware.md +21 -188
- package/templates/hono/docs/library/hono/rpc.md +40 -341
- package/templates/hono/docs/library/hono/validation.md +35 -195
- package/templates/hono/docs/library/pino/index.md +42 -333
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +64 -367
- package/templates/hono/docs/library/prisma/config.md +19 -260
- package/templates/hono/docs/library/prisma/index.md +67 -320
- package/templates/hono/docs/library/zod/index.md +53 -257
- package/templates/npx/CLAUDE.md +62 -274
- package/templates/npx/docs/references/patterns.md +160 -0
- package/templates/tanstack-start/CLAUDE.md +100 -256
- package/templates/tanstack-start/docs/architecture/architecture.md +44 -589
- package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
- package/templates/tanstack-start/docs/deployment/index.md +57 -286
- package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
- package/templates/tanstack-start/docs/deployment/railway.md +40 -409
- package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
- package/templates/tanstack-start/docs/design/components.md +77 -311
- package/templates/tanstack-start/docs/design/index.md +113 -69
- package/templates/tanstack-start/docs/design/safe-area.md +51 -250
- package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
- package/templates/tanstack-start/docs/guides/conventions.md +103 -0
- package/templates/tanstack-start/docs/guides/env-setup.md +34 -340
- package/templates/tanstack-start/docs/guides/getting-started.md +22 -209
- package/templates/tanstack-start/docs/guides/hooks.md +166 -0
- package/templates/tanstack-start/docs/guides/routes.md +166 -0
- package/templates/tanstack-start/docs/guides/services.md +143 -0
- package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
- package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
- package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
- package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
- package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
- package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
- package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
- package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
- package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
- package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
- package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
- package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
- package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +26 -97
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
- package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
- package/templates/tanstack-start/docs/library/zod/index.md +31 -144
- package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
- package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
- package/templates/hono/docs/commands/git.md +0 -145
- package/templates/hono/docs/mcp/context7.md +0 -106
- package/templates/hono/docs/mcp/index.md +0 -176
- package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
- package/templates/hono/docs/mcp/serena.md +0 -269
- package/templates/hono/docs/mcp/sgrep.md +0 -105
- package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
- package/templates/npx/docs/commands/git.md +0 -145
- package/templates/npx/docs/mcp/index.md +0 -60
- package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
- package/templates/tanstack-start/docs/commands/git.md +0 -145
- package/templates/tanstack-start/docs/design/accessibility.md +0 -433
- package/templates/tanstack-start/docs/design/color.md +0 -235
- package/templates/tanstack-start/docs/design/spacing.md +0 -341
- package/templates/tanstack-start/docs/design/typography.md +0 -324
- package/templates/tanstack-start/docs/guides/best-practices.md +0 -950
- package/templates/tanstack-start/docs/guides/husky-lint-staged.md +0 -303
- package/templates/tanstack-start/docs/guides/prettier.md +0 -189
- package/templates/tanstack-start/docs/guides/project-templates.md +0 -710
- package/templates/tanstack-start/docs/library/tanstack-query/setup.md +0 -107
- package/templates/tanstack-start/docs/library/zod/basic-types.md +0 -186
- package/templates/tanstack-start/docs/mcp/context7.md +0 -204
- package/templates/tanstack-start/docs/mcp/index.md +0 -177
- package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
- package/templates/tanstack-start/docs/mcp/serena.md +0 -269
- package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
|
@@ -6,16 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
## HTTPException
|
|
8
8
|
|
|
9
|
-
### 기본 사용
|
|
10
|
-
|
|
11
9
|
```typescript
|
|
12
|
-
import { Hono } from 'hono'
|
|
13
10
|
import { HTTPException } from 'hono/http-exception'
|
|
14
11
|
|
|
15
|
-
const app = new Hono()
|
|
16
|
-
|
|
17
12
|
app.get('/users/:id', async (c) => {
|
|
18
|
-
const id = c.req.param('id')
|
|
19
13
|
const user = await prisma.user.findUnique({ where: { id } })
|
|
20
14
|
|
|
21
15
|
if (!user) {
|
|
@@ -26,49 +20,22 @@ app.get('/users/:id', async (c) => {
|
|
|
26
20
|
})
|
|
27
21
|
```
|
|
28
22
|
|
|
29
|
-
###
|
|
23
|
+
### 일반적인 상태 코드
|
|
30
24
|
|
|
31
25
|
```typescript
|
|
32
|
-
throw new HTTPException(400, {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
// 400 Bad Request - 잘못된 요청
|
|
42
|
-
throw new HTTPException(400, { message: 'Invalid input' })
|
|
43
|
-
|
|
44
|
-
// 401 Unauthorized - 인증 필요
|
|
45
|
-
throw new HTTPException(401, { message: 'Authentication required' })
|
|
46
|
-
|
|
47
|
-
// 403 Forbidden - 권한 없음
|
|
48
|
-
throw new HTTPException(403, { message: 'Access denied' })
|
|
49
|
-
|
|
50
|
-
// 404 Not Found - 리소스 없음
|
|
51
|
-
throw new HTTPException(404, { message: 'Resource not found' })
|
|
52
|
-
|
|
53
|
-
// 409 Conflict - 충돌
|
|
54
|
-
throw new HTTPException(409, { message: 'Resource already exists' })
|
|
55
|
-
|
|
56
|
-
// 422 Unprocessable Entity - 검증 실패
|
|
57
|
-
throw new HTTPException(422, { message: 'Validation failed' })
|
|
58
|
-
|
|
59
|
-
// 429 Too Many Requests - 요청 제한 초과
|
|
60
|
-
throw new HTTPException(429, { message: 'Rate limit exceeded' })
|
|
61
|
-
|
|
62
|
-
// 500 Internal Server Error - 서버 에러
|
|
63
|
-
throw new HTTPException(500, { message: 'Internal server error' })
|
|
26
|
+
throw new HTTPException(400, { message: 'Invalid input' }) // Bad Request
|
|
27
|
+
throw new HTTPException(401, { message: 'Unauthorized' }) // 인증 필요
|
|
28
|
+
throw new HTTPException(403, { message: 'Access denied' }) // 권한 없음
|
|
29
|
+
throw new HTTPException(404, { message: 'Not found' }) // 리소스 없음
|
|
30
|
+
throw new HTTPException(409, { message: 'Already exists' }) // 충돌
|
|
31
|
+
throw new HTTPException(422, { message: 'Validation failed' }) // 검증 실패
|
|
32
|
+
throw new HTTPException(429, { message: 'Rate limit exceeded' })// 요청 제한
|
|
64
33
|
```
|
|
65
34
|
|
|
66
35
|
---
|
|
67
36
|
|
|
68
37
|
## 글로벌 에러 핸들러
|
|
69
38
|
|
|
70
|
-
### onError
|
|
71
|
-
|
|
72
39
|
```typescript
|
|
73
40
|
import { Hono } from 'hono'
|
|
74
41
|
import { HTTPException } from 'hono/http-exception'
|
|
@@ -76,144 +43,45 @@ import { HTTPException } from 'hono/http-exception'
|
|
|
76
43
|
const app = new Hono()
|
|
77
44
|
|
|
78
45
|
app.onError((err, c) => {
|
|
79
|
-
console.error(
|
|
80
|
-
|
|
81
|
-
// HTTPException 처리
|
|
82
|
-
if (err instanceof HTTPException) {
|
|
83
|
-
return c.json(
|
|
84
|
-
{
|
|
85
|
-
error: err.message,
|
|
86
|
-
status: err.status,
|
|
87
|
-
},
|
|
88
|
-
err.status
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 기타 에러
|
|
93
|
-
return c.json(
|
|
94
|
-
{
|
|
95
|
-
error: 'Internal Server Error',
|
|
96
|
-
message: err.message,
|
|
97
|
-
},
|
|
98
|
-
500
|
|
99
|
-
)
|
|
100
|
-
})
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 상세 에러 응답
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
app.onError((err, c) => {
|
|
107
|
-
const requestId = c.get('requestId')
|
|
46
|
+
console.error(err)
|
|
108
47
|
|
|
109
|
-
// HTTPException
|
|
110
48
|
if (err instanceof HTTPException) {
|
|
111
|
-
return c.json(
|
|
112
|
-
{
|
|
113
|
-
success: false,
|
|
114
|
-
error: {
|
|
115
|
-
code: err.status,
|
|
116
|
-
message: err.message,
|
|
117
|
-
requestId,
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
err.status
|
|
121
|
-
)
|
|
49
|
+
return c.json({ error: err.message, status: err.status }, err.status)
|
|
122
50
|
}
|
|
123
51
|
|
|
124
|
-
|
|
125
|
-
if (err.name === 'ZodError') {
|
|
126
|
-
return c.json(
|
|
127
|
-
{
|
|
128
|
-
success: false,
|
|
129
|
-
error: {
|
|
130
|
-
code: 400,
|
|
131
|
-
message: 'Validation failed',
|
|
132
|
-
details: err.flatten(),
|
|
133
|
-
requestId,
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
400
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// 개발 환경에서만 스택 트레이스 포함
|
|
141
|
-
const isDev = c.env.NODE_ENV === 'development'
|
|
142
|
-
|
|
143
|
-
return c.json(
|
|
144
|
-
{
|
|
145
|
-
success: false,
|
|
146
|
-
error: {
|
|
147
|
-
code: 500,
|
|
148
|
-
message: 'Internal Server Error',
|
|
149
|
-
...(isDev && { stack: err.stack }),
|
|
150
|
-
requestId,
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
500
|
|
154
|
-
)
|
|
52
|
+
return c.json({ error: 'Internal Server Error' }, 500)
|
|
155
53
|
})
|
|
156
|
-
```
|
|
157
54
|
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## 404 핸들러
|
|
161
|
-
|
|
162
|
-
### notFound
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
55
|
app.notFound((c) => {
|
|
166
|
-
return c.json(
|
|
167
|
-
{
|
|
168
|
-
error: 'Not Found',
|
|
169
|
-
path: c.req.path,
|
|
170
|
-
method: c.req.method,
|
|
171
|
-
},
|
|
172
|
-
404
|
|
173
|
-
)
|
|
56
|
+
return c.json({ error: 'Not Found', path: c.req.path }, 404)
|
|
174
57
|
})
|
|
175
58
|
```
|
|
176
59
|
|
|
177
60
|
---
|
|
178
61
|
|
|
179
|
-
##
|
|
62
|
+
## 상세 에러 응답
|
|
180
63
|
|
|
181
64
|
```typescript
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
api.onError((err, c) => {
|
|
186
|
-
console.log('API-specific error handler')
|
|
65
|
+
app.onError((err, c) => {
|
|
66
|
+
const requestId = c.get('requestId')
|
|
67
|
+
const isDev = c.env.NODE_ENV === 'development'
|
|
187
68
|
|
|
188
69
|
if (err instanceof HTTPException) {
|
|
189
|
-
return c.json({
|
|
70
|
+
return c.json({
|
|
71
|
+
success: false,
|
|
72
|
+
error: { status: err.status, message: err.message, requestId },
|
|
73
|
+
}, err.status)
|
|
190
74
|
}
|
|
191
75
|
|
|
192
|
-
return c.json({
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## 미들웨어에서 에러 접근
|
|
205
|
-
|
|
206
|
-
### c.error 사용
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
app.use(async (c, next) => {
|
|
210
|
-
await next()
|
|
211
|
-
|
|
212
|
-
// 핸들러에서 발생한 에러 접근
|
|
213
|
-
if (c.error) {
|
|
214
|
-
// 로깅, 모니터링 등
|
|
215
|
-
console.error('Error occurred:', c.error)
|
|
216
|
-
}
|
|
76
|
+
return c.json({
|
|
77
|
+
success: false,
|
|
78
|
+
error: {
|
|
79
|
+
status: 500,
|
|
80
|
+
message: 'Internal Server Error',
|
|
81
|
+
requestId,
|
|
82
|
+
...(isDev && { stack: err.stack }),
|
|
83
|
+
},
|
|
84
|
+
}, 500)
|
|
217
85
|
})
|
|
218
86
|
```
|
|
219
87
|
|
|
@@ -221,7 +89,7 @@ app.use(async (c, next) => {
|
|
|
221
89
|
|
|
222
90
|
## 커스텀 에러 클래스
|
|
223
91
|
|
|
224
|
-
### errors.ts
|
|
92
|
+
### lib/errors.ts
|
|
225
93
|
|
|
226
94
|
```typescript
|
|
227
95
|
import { HTTPException } from 'hono/http-exception'
|
|
@@ -238,18 +106,6 @@ export class UnauthorizedError extends HTTPException {
|
|
|
238
106
|
}
|
|
239
107
|
}
|
|
240
108
|
|
|
241
|
-
export class ForbiddenError extends HTTPException {
|
|
242
|
-
constructor(message = 'Access denied') {
|
|
243
|
-
super(403, { message })
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export class ValidationError extends HTTPException {
|
|
248
|
-
constructor(message: string, public details?: unknown) {
|
|
249
|
-
super(422, { message })
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
109
|
export class ConflictError extends HTTPException {
|
|
254
110
|
constructor(resource: string) {
|
|
255
111
|
super(409, { message: `${resource} already exists` })
|
|
@@ -264,137 +120,20 @@ import { NotFoundError, ConflictError } from '@/lib/errors'
|
|
|
264
120
|
|
|
265
121
|
app.get('/users/:id', async (c) => {
|
|
266
122
|
const user = await prisma.user.findUnique({ where: { id } })
|
|
267
|
-
|
|
268
|
-
if (!user) {
|
|
269
|
-
throw new NotFoundError('User')
|
|
270
|
-
}
|
|
271
|
-
|
|
123
|
+
if (!user) throw new NotFoundError('User')
|
|
272
124
|
return c.json({ user })
|
|
273
125
|
})
|
|
274
126
|
|
|
275
127
|
app.post('/users', async (c) => {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
if (existing) {
|
|
282
|
-
throw new ConflictError('User with this email')
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const user = await prisma.user.create({ data })
|
|
286
|
-
return c.json({ user }, 201)
|
|
287
|
-
})
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
## 스트리밍 에러 처리
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
import { stream } from 'hono/streaming'
|
|
296
|
-
|
|
297
|
-
app.get('/stream', (c) => {
|
|
298
|
-
return stream(
|
|
299
|
-
c,
|
|
300
|
-
async (stream) => {
|
|
301
|
-
stream.onAbort(() => {
|
|
302
|
-
console.log('Stream aborted')
|
|
303
|
-
})
|
|
304
|
-
|
|
305
|
-
await stream.write(new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]))
|
|
306
|
-
},
|
|
307
|
-
// 에러 핸들러 (선택적)
|
|
308
|
-
(err, stream) => {
|
|
309
|
-
stream.writeln('An error occurred!')
|
|
310
|
-
console.error(err)
|
|
311
|
-
}
|
|
312
|
-
)
|
|
128
|
+
const existing = await prisma.user.findUnique({ where: { email } })
|
|
129
|
+
if (existing) throw new ConflictError('User with this email')
|
|
130
|
+
// ...
|
|
313
131
|
})
|
|
314
132
|
```
|
|
315
133
|
|
|
316
134
|
---
|
|
317
135
|
|
|
318
|
-
## 완전한 에러 처리 설정
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
import { Hono } from 'hono'
|
|
322
|
-
import { HTTPException } from 'hono/http-exception'
|
|
323
|
-
import { logger } from 'hono/logger'
|
|
324
|
-
import { requestId } from 'hono/request-id'
|
|
325
|
-
|
|
326
|
-
type Env = {
|
|
327
|
-
Bindings: {
|
|
328
|
-
NODE_ENV: string
|
|
329
|
-
}
|
|
330
|
-
Variables: {
|
|
331
|
-
requestId: string
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const app = new Hono<Env>()
|
|
336
|
-
|
|
337
|
-
// 미들웨어
|
|
338
|
-
app.use(logger())
|
|
339
|
-
app.use(requestId())
|
|
340
|
-
|
|
341
|
-
// 글로벌 에러 핸들러
|
|
342
|
-
app.onError((err, c) => {
|
|
343
|
-
const reqId = c.get('requestId')
|
|
344
|
-
const isDev = c.env.NODE_ENV === 'development'
|
|
345
|
-
|
|
346
|
-
console.error(`[${reqId}] Error:`, err)
|
|
347
|
-
|
|
348
|
-
if (err instanceof HTTPException) {
|
|
349
|
-
return c.json(
|
|
350
|
-
{
|
|
351
|
-
success: false,
|
|
352
|
-
error: {
|
|
353
|
-
status: err.status,
|
|
354
|
-
message: err.message,
|
|
355
|
-
requestId: reqId,
|
|
356
|
-
},
|
|
357
|
-
},
|
|
358
|
-
err.status
|
|
359
|
-
)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return c.json(
|
|
363
|
-
{
|
|
364
|
-
success: false,
|
|
365
|
-
error: {
|
|
366
|
-
status: 500,
|
|
367
|
-
message: 'Internal Server Error',
|
|
368
|
-
requestId: reqId,
|
|
369
|
-
...(isDev && { stack: err.stack }),
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
500
|
|
373
|
-
)
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
// 404 핸들러
|
|
377
|
-
app.notFound((c) => {
|
|
378
|
-
return c.json(
|
|
379
|
-
{
|
|
380
|
-
success: false,
|
|
381
|
-
error: {
|
|
382
|
-
status: 404,
|
|
383
|
-
message: 'Not Found',
|
|
384
|
-
path: c.req.path,
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
404
|
|
388
|
-
)
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
export default app
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
136
|
## 관련 문서
|
|
397
137
|
|
|
398
138
|
- [기본 사용법](./index.md)
|
|
399
139
|
- [미들웨어](./middleware.md)
|
|
400
|
-
- [Zod 검증](./validation.md)
|
|
@@ -1,73 +1,32 @@
|
|
|
1
1
|
# Hono - Web Framework
|
|
2
2
|
|
|
3
|
-
> Web Standards 기반
|
|
3
|
+
> Web Standards 기반 초경량 서버 프레임워크
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
@env-setup.md
|
|
6
|
+
@middleware.md
|
|
7
|
+
@validation.md
|
|
8
|
+
@error-handling.md
|
|
9
|
+
@rpc.md
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
## 설치
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
# npm
|
|
17
16
|
npm install hono
|
|
18
|
-
|
|
19
|
-
# bun
|
|
20
|
-
bun add hono
|
|
21
|
-
|
|
22
|
-
# yarn
|
|
23
|
-
yarn add hono
|
|
24
17
|
```
|
|
25
18
|
|
|
26
19
|
---
|
|
27
20
|
|
|
28
21
|
## 기본 사용법
|
|
29
22
|
|
|
30
|
-
### App 생성
|
|
31
|
-
|
|
32
23
|
```typescript
|
|
33
24
|
import { Hono } from 'hono'
|
|
34
25
|
|
|
35
26
|
const app = new Hono()
|
|
36
27
|
|
|
37
|
-
app.get('/', (c) =>
|
|
38
|
-
return c.text('Hello Hono!')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
export default app
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### HTTP 메서드
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
const app = new Hono()
|
|
48
|
-
|
|
49
|
-
app.get('/', (c) => c.text('GET /'))
|
|
28
|
+
app.get('/', (c) => c.text('Hello Hono!'))
|
|
50
29
|
app.post('/', (c) => c.text('POST /'))
|
|
51
|
-
app.put('/', (c) => c.text('PUT /'))
|
|
52
|
-
app.delete('/', (c) => c.text('DELETE /'))
|
|
53
|
-
app.patch('/', (c) => c.text('PATCH /'))
|
|
54
|
-
|
|
55
|
-
// 모든 메서드
|
|
56
|
-
app.all('/hello', (c) => c.text('Any Method /hello'))
|
|
57
|
-
|
|
58
|
-
// 여러 메서드
|
|
59
|
-
app.on(['PUT', 'DELETE'], '/post', (c) => c.text('PUT or DELETE /post'))
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## 라우팅
|
|
65
|
-
|
|
66
|
-
### 기본 라우팅
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
// 정적 경로
|
|
70
|
-
app.get('/users', (c) => c.json({ users: [] }))
|
|
71
30
|
|
|
72
31
|
// 동적 파라미터
|
|
73
32
|
app.get('/users/:id', (c) => {
|
|
@@ -80,34 +39,22 @@ app.get('/posts/:postId/comments/:commentId', (c) => {
|
|
|
80
39
|
const { postId, commentId } = c.req.param()
|
|
81
40
|
return c.json({ postId, commentId })
|
|
82
41
|
})
|
|
83
|
-
```
|
|
84
42
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// 와일드카드 매칭
|
|
89
|
-
app.get('/wild/*/card', (c) => {
|
|
90
|
-
return c.text('GET /wild/*/card')
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
// 모든 하위 경로
|
|
94
|
-
app.get('/api/*', (c) => {
|
|
95
|
-
return c.text('API catch-all')
|
|
96
|
-
})
|
|
43
|
+
export default app
|
|
97
44
|
```
|
|
98
45
|
|
|
99
|
-
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 라우트 그룹화
|
|
100
49
|
|
|
101
50
|
```typescript
|
|
102
51
|
const app = new Hono()
|
|
103
52
|
|
|
104
|
-
// 서브 라우트
|
|
105
53
|
const users = new Hono()
|
|
106
54
|
users.get('/', (c) => c.json({ users: [] }))
|
|
107
55
|
users.get('/:id', (c) => c.json({ id: c.req.param('id') }))
|
|
108
56
|
users.post('/', (c) => c.json({ created: true }, 201))
|
|
109
57
|
|
|
110
|
-
// 마운트
|
|
111
58
|
app.route('/users', users)
|
|
112
59
|
```
|
|
113
60
|
|
|
@@ -115,26 +62,16 @@ app.route('/users', users)
|
|
|
115
62
|
|
|
116
63
|
## Context (c)
|
|
117
64
|
|
|
118
|
-
### Request
|
|
65
|
+
### Request
|
|
119
66
|
|
|
120
67
|
```typescript
|
|
121
68
|
app.get('/info', (c) => {
|
|
122
|
-
// URL 정보
|
|
123
69
|
const url = c.req.url
|
|
124
70
|
const path = c.req.path
|
|
125
71
|
const method = c.req.method
|
|
126
|
-
|
|
127
|
-
// 파라미터
|
|
128
72
|
const id = c.req.param('id')
|
|
129
|
-
const params = c.req.param() // 모든 파라미터
|
|
130
|
-
|
|
131
|
-
// 쿼리 스트링
|
|
132
73
|
const page = c.req.query('page')
|
|
133
|
-
const queries = c.req.query() // 모든 쿼리
|
|
134
|
-
|
|
135
|
-
// 헤더
|
|
136
74
|
const auth = c.req.header('Authorization')
|
|
137
|
-
|
|
138
75
|
return c.json({ url, path, method })
|
|
139
76
|
})
|
|
140
77
|
```
|
|
@@ -142,86 +79,57 @@ app.get('/info', (c) => {
|
|
|
142
79
|
### Request Body
|
|
143
80
|
|
|
144
81
|
```typescript
|
|
145
|
-
// JSON
|
|
146
82
|
app.post('/json', async (c) => {
|
|
147
83
|
const body = await c.req.json()
|
|
148
84
|
return c.json(body)
|
|
149
85
|
})
|
|
150
86
|
|
|
151
|
-
// Form Data
|
|
152
87
|
app.post('/form', async (c) => {
|
|
153
88
|
const body = await c.req.parseBody()
|
|
154
89
|
return c.json(body)
|
|
155
90
|
})
|
|
156
|
-
|
|
157
|
-
// Raw Text
|
|
158
|
-
app.post('/text', async (c) => {
|
|
159
|
-
const text = await c.req.text()
|
|
160
|
-
return c.text(text)
|
|
161
|
-
})
|
|
162
91
|
```
|
|
163
92
|
|
|
164
|
-
### Response
|
|
93
|
+
### Response
|
|
165
94
|
|
|
166
95
|
```typescript
|
|
167
|
-
// Text
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// JSON with status
|
|
174
|
-
app.post('/created', (c) => c.json({ id: 1 }, 201))
|
|
175
|
-
|
|
176
|
-
// HTML
|
|
177
|
-
app.get('/html', (c) => c.html('<h1>Hello</h1>'))
|
|
178
|
-
|
|
179
|
-
// Redirect
|
|
180
|
-
app.get('/old', (c) => c.redirect('/new'))
|
|
181
|
-
|
|
182
|
-
// Not Found
|
|
183
|
-
app.get('/404', (c) => c.notFound())
|
|
96
|
+
c.text('Hello') // Text
|
|
97
|
+
c.json({ message: 'Hello' }) // JSON
|
|
98
|
+
c.json({ id: 1 }, 201) // JSON + status
|
|
99
|
+
c.html('<h1>Hello</h1>') // HTML
|
|
100
|
+
c.redirect('/new') // Redirect
|
|
101
|
+
c.notFound() // 404
|
|
184
102
|
```
|
|
185
103
|
|
|
186
104
|
---
|
|
187
105
|
|
|
188
|
-
##
|
|
106
|
+
## Bindings & Variables
|
|
189
107
|
|
|
190
108
|
```typescript
|
|
191
109
|
type Bindings = {
|
|
192
110
|
DATABASE_URL: string
|
|
193
111
|
JWT_SECRET: string
|
|
194
|
-
MY_BUCKET: R2Bucket // Cloudflare R2
|
|
195
112
|
}
|
|
196
113
|
|
|
197
|
-
const app = new Hono<{ Bindings: Bindings }>()
|
|
198
|
-
|
|
199
|
-
app.get('/', (c) => {
|
|
200
|
-
const dbUrl = c.env.DATABASE_URL
|
|
201
|
-
const secret = c.env.JWT_SECRET
|
|
202
|
-
return c.json({ connected: true })
|
|
203
|
-
})
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## Variables (상태 공유)
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
114
|
type Variables = {
|
|
212
115
|
userId: string
|
|
213
116
|
user: { id: string; name: string }
|
|
214
117
|
}
|
|
215
118
|
|
|
216
|
-
const app = new Hono<{ Variables: Variables }>()
|
|
119
|
+
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()
|
|
120
|
+
|
|
121
|
+
// 환경 변수 접근
|
|
122
|
+
app.get('/', (c) => {
|
|
123
|
+
const dbUrl = c.env.DATABASE_URL
|
|
124
|
+
return c.json({ connected: true })
|
|
125
|
+
})
|
|
217
126
|
|
|
218
|
-
//
|
|
127
|
+
// Variables 설정/사용
|
|
219
128
|
app.use(async (c, next) => {
|
|
220
129
|
c.set('userId', '123')
|
|
221
130
|
await next()
|
|
222
131
|
})
|
|
223
132
|
|
|
224
|
-
// 핸들러에서 사용
|
|
225
133
|
app.get('/me', (c) => {
|
|
226
134
|
const userId = c.get('userId')
|
|
227
135
|
return c.json({ userId })
|
|
@@ -232,7 +140,7 @@ app.get('/me', (c) => {
|
|
|
232
140
|
|
|
233
141
|
## 관련 문서
|
|
234
142
|
|
|
235
|
-
- [환경 변수 설정](./env-setup.md)
|
|
143
|
+
- [환경 변수 설정](./env-setup.md)
|
|
236
144
|
- [미들웨어](./middleware.md)
|
|
237
145
|
- [Zod 검증](./validation.md)
|
|
238
146
|
- [에러 처리](./error-handling.md)
|