Skip to main content

hono-dev

This skill should be used when building APIs with Hono, using hc client, implementing OpenAPI, or when "Hono", "RPC", or "type-safe API" are mentioned.

소스 정보

저장소
outfitter-dev/agents
최근 소스 활동
2026년 1월 27일 15:45
감지된 SKILL.md 언어
영어
스타
28
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
7 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
hono-dev
description
This skill should be used when building APIs with Hono, using hc client, implementing OpenAPI, or when "Hono", "RPC", or "type-safe API" are mentioned.
metadata
{"version":"1.0.0"}
# Hono API Development Route chaining → type-safe RPC → end-to-end types. <when_to_use> - Building REST APIs with Hono - Type-safe RPC with hono/client - OpenAPI documentation with Zod - Testing APIs with testClient - When user mentions "Hono", "RPC", or "OpenAPI" NOT for: Bun runtime APIs (use bun-dev), other frameworks (Express, Fastify) </when_to_use> <version_notes> Hono v4+ with @hono/zod-openapi v1.0+ Check hono.dev for latest patterns. </version_notes> ## Route Chaining — Critical Pattern Type inference flows through method chain. Break chain = lose types. <route_chaining> ```typescript // ✅ Chained routes preserve types const app = new Hono() .get('/users', (c) => c.json({ users: [] })) .get('/users/:id', (c) => { const id = c.req.param('id'); // Typed! return c.json({ id }); }) .post('/users', async (c) => { const body = await c.req.json(); return c.json({ created: true }, 201); }); export type AppType = typeof app; // Full route types! ``` **❌ NEVER break the chain:** ```typescript const app = new Hono(); app.get('/users', handler1); // Types LOST! app.post('/users', handler2); ``` **Path parameters** — typed automatically: ```typescript .get('/posts/:id/comments/:commentId', (c) => { const { id, commentId } = c.req.param(); // Both string return c.json({ postId: id, commentId }); }) ``` **Query parameters** — use Zod for validation: ```typescript import { zValidator } from '@hono/zod-validator'; import { z } from 'zod'; const QuerySchema = z.object({ page: z.coerce.number().int().positive().default(1), limit: z.coerce.number().int().positive().max(100).default(20), }); const app = new Hono() .get('/search', zValidator('query', QuerySchema), (c) => { const { page, limit } = c.req.valid('query'); // Fully typed! return c.json({ page, limit }); }); ``` **Middleware in chain:** ```typescript const app = new Hono() .use('*', logger()) .use('/api/*', cors()) .get('/api/public', (c) => c.json({ public: true })) .use('/api/admin/*', authMiddleware) .get('/api/admin/users', (c) => c.json({ users: [] })); ``` </route_chaining> ## Factory Pattern — Context Typing Use `createFactory<Env>()` to type context variables across middleware and routes. <factory_pattern> ```typescript import { createFactory } from 'hono/factory'; import type { Database } from 'bun:sqlite'; type Env = { Variables: { user: { id: string; role: 'admin' | 'user' }; requestId: string; db: Database; }; }; const factory = createFactory<Env>(); // Typed middleware const authMiddleware = factory.createMiddleware(async (c, next) => { const token = c.req.header('authorization')?.replace('Bearer ', ''); if (!token) throw new HTTPException(401, { message: 'Unauthorized' }); const user = await verifyToken(token); c.set('user', user); // Type-checked! await next(); }); // Typed handlers const getProfile = factory.createHandlers((c) => { const user = c.get('user'); // Typed: { id: string; role: 'admin' | 'user' } return c.json({ user }); }); // Assemble app const app = factory.createApp() .use('*', dbMiddleware) .use('/api/*', authMiddleware) .get('/api/profile', ...getProfile); export type AppType = typeof app; ``` **Multi-module structure:** ```typescript // routes/users.ts export const usersRoute = factory.createApp() .get('/', (c) => c.json({ users: [] })) .post('/', zValidator('json', CreateUserSchema), async (c) => { const data = c.req.valid('json'); return c.json({ created: true }, 201); }); // index.ts const app = factory.createApp() .use('*', dbMiddleware) .route('/users', usersRoute) .route('/posts', postsRoute); ``` See [factory-pattern.md](references/factory-pattern.md) for advanced patterns. </factory_pattern> ## Error Handling <error_handling> ```typescript import { HTTPException } from 'hono/http-exception'; // Throw typed errors app.get('/users/:id', async (c) => { const user = await findUser(c.req.param('id')); if (!user) { throw new HTTPException(404, { message: 'User not found' }); } return c.json({ user }); }); // Custom error classes class NotFoundError extends HTTPException { constructor(resource: string) { super(404, { message: `${resource} not found` }); } } class UnauthorizedError extends HTTPException { constructor(message = 'Unauthorized') { super(401, { message }); } } // Centralized handler app.onError((err, c) => { if (err instanceof HTTPException) { return c.json({ error: err.message }, err.status); } if (err instanceof ZodError) { return c.json({ error: 'Validation failed', issues: err.issues.map(i => ({ path: i.path.join('.'), message: i.message })) }, 400); } const isDev = Bun.env.NODE_ENV !== 'production'; return c.json({ error: isDev ? err.message : 'Internal server error' }, 500); }); app.notFound((c) => c.json({ error: 'Not found', path: c.req.path }, 404)); ``` See [error-handling.md](references/error-handling.md) for patterns. </error_handling> ## Zod OpenAPI <zod_openapi> ```typescript import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi'; import { swaggerUI } from '@hono/swagger-ui'; const UserSchema = z.object({ id: z.string().uuid(), email: z.string().email(), name: z.string().min(1).max(100), }).openapi('User'); const route = createRoute({ method: 'get', path: '/users/{id}', request: { params: z.object({ id: z.string().uuid() }), }, responses: { 200: { content: { 'application/json': { schema: UserSchema } }, description: 'User found', }, 404: { content: { 'application/json': { schema: z.object({ error: z.string() }) } }, description: 'User not found', }, }, tags: ['Users'], summary: 'Get user by ID', }); const app = new OpenAPIHono(); app.openapi(route, (c) => { const { id } = c.req.valid('param'); // Typed! const user = db.query('SELECT * FROM users WHERE id = ?').get(id); if (!user) return c.json({ error: 'User not found' }, 404); return c.json(user, 200); }); // Swagger UI app.get('/docs', swaggerUI({ url: '/openapi.json' })); app.doc('/openapi.json', { openapi: '3.1.0', info: { title: 'API', version: '1.0.0' }, }); ``` See [zod-openapi.md](references/zod-openapi.md) for complete patterns. </zod_openapi> ## RPC Client — End-to-End Types <rpc_client> ```typescript // Server const app = new Hono() .get('/posts', (c) => c.json({ posts: [] })) .get('/posts/:id', (c) => c.json({ id: c.req.param('id') })) .post('/posts', zValidator('json', CreatePostSchema), async (c) => { const data = c.req.valid('json'); return c.json({ id: '123', ...data }, 201); }); export type AppType = typeof app; // Client import { hc } from 'hono/client'; import type { AppType } from './server'; const client = hc<AppType>('http://localhost:3000'); // GET request const res = await client.posts.$get(); const data = await res.json(); // Typed: { posts: any[] } // GET with params const res2 = await client.posts[':id'].$get({ param: { id: '123' } }); // POST request const res3 = await client.posts.$post({ json: { title: 'Hello', content: 'World' } }); // With headers const res4 = await client.posts.$get({}, { headers: { Authorization: 'Bearer token' } }); ``` </rpc_client> ## Testing with testClient <testing> ```typescript import { describe, expect, test, beforeEach, afterEach } from 'bun:test'; import { testClient } from 'hono/testing'; import { Database } from 'bun:sqlite'; import app from './server'; describe('API Tests', () => { let db: Database; beforeEach(() => { db = new Database(':memory:'); db.run('CREATE TABLE posts (id TEXT PRIMARY KEY, title TEXT, content TEXT)'); }); afterEach(() => { db.close(); }); const client = testClient(app); test('GET /posts returns posts', async () => { const res = await client.posts.$get(); expect(res.status).toBe(200); const data = await res.json(); expect(data).toHaveProperty('posts'); }); test('POST /posts creates post', async () => { const res = await client.posts.$post({ json: { title: 'Test', content: 'Content' } }); expect(res.status).toBe(201); const data = await res.json();
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기