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
آخر نشاط في المصدر
٢٧ يناير ٢٠٢٦ في ١٥:٤٥
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٨
التفرعات
١

خيارات التثبيت

يُحدَّد 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