AgentSkillsCN

Framework

框架

SKILL.md

Framework Skill

This skill documents the Hono + Bun framework patterns used in this project.

Tech Stack

  • Runtime: Bun (TypeScript/JavaScript runtime)
  • Web Framework: Hono (lightweight, fast web framework)
  • ORM: Lucid (AdonisJS ORM, standalone setup)
  • Database: MySQL (production), SQLite (testing)
  • DI Container: Typedi (dependency injection)
  • Validation: VineJS
  • File Storage: Flydrive
  • API Docs: openapi-metadata

Request Flow

code
Request → Middleware → Routes → Controller → Service → Model → Response
                                    ↓
                              Error Handler

Entry Point (server.ts)

typescript
import "reflect-metadata"  // Required for Typedi
import "@/utils/sentry"    // Error tracking
import { Hono } from "hono"
import { vineValidation } from "./app/middleware/vine_validation_middleware"
import api from "./routes/api"
import web from "./routes/web"
import errorHandler from "./app/error-handler"

const app = new Hono()

// Global middleware
app.use(vineValidation)

// Routes
app.route("/", web)
app.route("/", api)

// Error handler
app.onError(errorHandler)

export default { port: 3000, fetch: app.fetch }

Hono Basics

Route Definition

typescript
import { Hono } from "hono"

const app = new Hono()

// Basic routes
app.get("/", (c) => c.text("Hello"))
app.post("/users", (c) => c.json({ id: 1 }))

// Route parameters
app.get("/users/:id", (c) => {
  const id = c.req.param("id")
  return c.json({ id })
})

// Query parameters
app.get("/search", (c) => {
  const q = c.req.query("q")
  return c.json({ query: q })
})

// Base path
const api = new Hono().basePath("/api")
api.get("/users", handler)  // /api/users

// Mount routes
app.route("/", api)

Context Object

typescript
app.get("/example", (c) => {
  // Request info
  c.req.url          // Full URL
  c.req.method       // HTTP method
  c.req.param("id")  // Route parameter
  c.req.query("q")   // Query parameter
  c.req.queries()    // All query params
  c.req.header("x-custom")  // Header value

  // Request body
  await c.req.json()      // JSON body
  await c.req.formData()  // Form data

  // Responses
  return c.json({ data })     // JSON response
  return c.text("Hello")      // Text response
  return c.html("<h1>Hi</h1>")// HTML response
  return c.redirect("/other") // Redirect
  return c.notFound()         // 404 response

  // Custom response
  return new Response("Custom", { status: 201 })
})

Middleware

typescript
import { Next } from "hono"
import type { Context } from "hono"

// Global middleware
app.use(async (c: Context, next: Next) => {
  console.log("Before request")
  await next()
  console.log("After request")
})

// Path-specific middleware
app.use("/api/*", authMiddleware)

// Multiple middleware
app.use("/admin/*", authMiddleware, adminMiddleware)

Controller Helper

The Controller() helper resolves controllers from Typedi and handles responses:

typescript
// utils/index.ts
export function Controller<T>(
  controller: Constructor<T>,
  func: keyof T
): (c: Context) => Promise<Response> {
  const instance = Container.get(controller)
  return async (c: Context) => {
    const data = await (instance[func] as any)(c)
    if (data instanceof Response) return data
    if (data instanceof String) return c.text(data)
    return c.json(data)
  }
}

// Usage in routes
route.get("/users", Controller(UserController, "getUsers"))

Dependency Injection (Typedi)

typescript
import { Service, Container } from "typedi"

// Mark classes with @Service()
@Service()
export default class UserService {
  async getUsers() {
    return await User.query()
  }
}

@Service()
export default class UserController {
  // Constructor injection
  constructor(private readonly userService: UserService) {}

  async getUsers() {
    return this.userService.getUsers()
  }
}

// Manual resolution
const instance = Container.get(UserController)

Validation Middleware

typescript
// Adds req.validate() to all requests
export const vineValidation = async ({ req }: Context, next: Next) => {
  (req as any).validate = async <T>(validator: VineValidator): Promise<T> => {
    // Collects data from JSON, form-data, and query params
    const payload = { ...params, ...jsonBody, ...formData }
    return await validator.validate(payload)
  }
  await next()
}

// Usage in controller
const payload = await req.validate(myValidator)
// payload is fully typed!

Error Handling

typescript
// app/error-handler.ts
export default function errorHandler(err: Error, c: Context) {
  // Validation errors -> 422
  if (err instanceof errors.E_VALIDATION_ERROR) {
    return c.json({
      success: false,
      message: err.message,
      errors: err.messages
    }, 422)
  }

  // Other errors -> 500
  return c.json({
    success: false,
    message: err.message || "Internal server error"
  }, 500)
}

File Storage (Flydrive)

typescript
import { disk } from "@/utils/disk"

// Store file
const fileName = `uploads/${randomUUIDv7()}.png`
await disk.put(fileName, content)

// Get URL
const url = await disk.getUrl(fileName)

// Delete file
await disk.delete(fileName)

// Check exists
const exists = await disk.exists(fileName)

Environment Variables

bash
# Required
APP_ENV=development|production|test
DATABASE_URL=mysql://user:pass@host:port/database
DRIVE_DISK=fs

# Optional
SENTRY_DSN=https://...
SENTRY_TRACES_SAMPLE_RATE=0.1

Development Commands

bash
bun run dev          # Start dev server with hot reload
bun run format       # Format code with Biome
bun run lint         # Lint and auto-fix
bun run test         # Run tests
bun run build:prod   # Build production executable

Project Structure

code
├── app/
│   ├── controllers/     # Web controllers (views)
│   ├── middleware/      # Global middleware
│   ├── modules/         # Feature modules
│   │   └── {module}/
│   │       ├── {module}.controller.ts
│   │       ├── {module}.service.ts
│   │       ├── {module}.model.ts
│   │       └── {module}.validator.ts
│   └── error-handler.ts
├── config/              # Configuration files
├── migrations/          # Database migrations
├── routes/              # Route definitions
├── test/                # Tests
├── utils/               # Utilities
├── views/               # View templates
├── server.ts            # Entry point
└── knexfile.ts          # Knex config