Hono 示例

Hono超快Web框架示例,包括路由、中间件和部署

💻 Hono Hello World typescript

🟢 simple

Hono Web框架基础设置和Hello World应用程序

// Hono Hello World Examples

import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'

// 1. Basic Hono application
const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello, World!')
})

app.get('/hello', (c) => {
  return c.text('Hello from Hono!')
})

app.get('/hello/:name', (c) => {
  const name = c.req.param('name')
  return c.text(`Hello, ${name}!`)
})

// Start the server
// Deno: deno run --allow-net app.ts
// Node: npm start
// Bun: bun run app.ts

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

// 2. Hono with JSON responses
const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello, Hono!')
})

app.get('/api/hello', (c) => {
  return c.json({
    message: 'Hello, World!',
    timestamp: new Date().toISOString(),
    version: '1.0.0',
    framework: 'Hono'
  })
})

app.get('/api/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({
    id: parseInt(id),
    name: `User ${id}`,
    email: `user${id}@example.com`,
    createdAt: new Date().toISOString()
  })
})

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

// 3. Hono with HTTP methods
const app = new Hono()

// GET routes
app.get('/api/users', (c) => {
  return c.json([
    { id: 1, name: 'John Doe', email: '[email protected]' },
    { id: 2, name: 'Jane Smith', email: '[email protected]' },
    { id: 3, name: 'Bob Johnson', email: '[email protected]' }
  ])
})

app.get('/api/users/:id', (c) => {
  const id = parseInt(c.req.param('id'))
  const users = [
    { id: 1, name: 'John Doe', email: '[email protected]' },
    { id: 2, name: 'Jane Smith', email: '[email protected]' },
    { id: 3, name: 'Bob Johnson', email: '[email protected]' }
  ]
  const user = users.find(u => u.id === id)

  if (!user) {
    return c.json({ error: 'User not found' }, 404)
  }

  return c.json(user)
})

// POST route
app.post('/api/users', async (c) => {
  const body = await c.req.json()
  const newUser = {
    id: Date.now(),
    name: body.name,
    email: body.email,
    createdAt: new Date().toISOString()
  }

  return c.json(newUser, 201)
})

// PUT route
app.put('/api/users/:id', async (c) => {
  const id = parseInt(c.req.param('id'))
  const body = await c.req.json()

  return c.json({
    id,
    ...body,
    updatedAt: new Date().toISOString()
  })
})

// DELETE route
app.delete('/api/users/:id', (c) => {
  const id = parseInt(c.req.param('id'))

  return c.json({ message: `User ${id} deleted successfully` })
})

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

// 4. Hono with request parameters
const app = new Hono()

// Query parameters
app.get('/search', (c) => {
  const query = c.req.query('q')
  const limit = parseInt(c.req.query('limit') || '10')
  const page = parseInt(c.req.query('page') || '1')

  return c.json({
    query: query || 'No query provided',
    limit,
    page,
    results: query ? [`Result for "${query}"`] : []
  })
})

// Headers
app.get('/info', (c) => {
  const userAgent = c.req.header('User-Agent')
  const accept = c.req.header('Accept')
  const host = c.req.header('Host')

  return c.json({
    userAgent,
    accept,
    host,
    ip: c.req.header('x-forwarded-for') || 'unknown'
  })
})

// Request body
app.post('/api/data', async (c) => {
  const body = await c.req.json()
  const contentType = c.req.header('Content-Type')

  return c.json({
    received: body,
    contentType,
    timestamp: new Date().toISOString()
  })
})

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

// 5. Hono with different response types
const app = new Hono()

// HTML response
app.get('/html', (c) => {
  const html = `
    <!DOCTYPE html>
    <html>
    <head>
      <title>Hello Hono</title>
      <style>
        body { font-family: Arial, sans-serif; margin: 40px; }
        h1 { color: #ff6b6b; }
      </style>
    </head>
    <body>
      <h1>Hello, World!</h1>
      <p>This is an HTML response from Hono framework</p>
      <p>Current time: ${new Date().toLocaleTimeString()}</p>
    </body>
    </html>
  `

  return c.html(html)
})

// Redirect
app.get('/redirect', (c) => {
  return c.redirect('/')
})

// Custom status
app.get('/custom-status', (c) => {
  return c.text("I'm a teapot", 418)
})

// File download
app.get('/download', (c) => {
  const content = "Hello, this is a downloadable file!"

  return new Response(content, {
    headers: {
      'Content-Disposition': 'attachment; filename="hello.txt"',
      'Content-Type': 'text/plain'
    }
  })
})

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

// 6. Hono with error handling
const app = new Hono()

// Error handling middleware
app.onError((err, c) => {
  console.error(`Error: ${err.message}`)

  return c.json({
    error: 'Internal Server Error',
    message: err.message,
    timestamp: new Date().toISOString()
  }, 500)
})

// Not found handler
app.notFound((c) => {
  return c.json({
    error: 'Not Found',
    message: `Route ${c.req.path} not found`,
    availableRoutes: ['/hello', '/api/users', '/search']
  }, 404)
})

// Routes that might throw errors
app.get('/', (c) => {
  return c.text('Hello, World!')
})

app.get('/error', () => {
  throw new Error('This is a test error!')
})

app.get('/async-error', async () => {
  await new Promise(resolve => setTimeout(resolve, 100))
  throw new Error('This is an async error!')
})

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

// 7. Hono with chaining
const app = new Hono()

// Route chaining
app
  .get('/users/:id', (c) => {
    const id = c.req.param('id')
    return c.text(`Getting user ${id}`)
  })
  .put('/users/:id', async (c) => {
    const id = c.req.param('id')
    const body = await c.req.json()
    return c.json({ id, ...body })
  })
  .delete('/users/:id', (c) => {
    const id = c.req.param('id')
    return c.text(`Deleting user ${id}`)
  })

// Middleware chaining
app.use('*', logger())
app.use('*', cors({
  origin: '*',
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
}))

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

// 8. Hono with environment variables
const app = new Hono()

// Get environment variables
const PORT = parseInt(process.env.PORT || '3000')
const NODE_ENV = process.env.NODE_ENV || 'development'

app.get('/', (c) => {
  return c.text(`Hono server running in ${NODE_ENV} mode`)
})

app.get('/env', (c) => {
  return c.json({
    port: PORT,
    nodeEnv: NODE_ENV,
    isDevelopment: NODE_ENV === 'development',
    isProduction: NODE_ENV === 'production'
  })
})

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

// For Deno deployment (e.g., Deno Deploy)
Deno.serve(app.fetch)

💻 Hono 中间件 typescript

🟡 intermediate

中间件示例,包括日志记录、CORS、身份验证和自定义中间件

// Hono Middleware Examples

import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { bearerAuth } from 'hono/bearer-auth'
import { jwt } from 'hono/jwt'

// 1. Basic middleware setup
const app = new Hono()

// Built-in logger middleware
app.use('*', logger())

// CORS middleware
app.use('/api/*', cors({
  origin: ['http://localhost:3000', 'https://example.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowHeaders: ['Content-Type', 'Authorization'],
  exposeHeaders: ['X-Total-Count']
}))

// Bearer authentication middleware
app.use('/protected/*', bearerAuth({ token: 'secret-token' }))

// Routes
app.get('/', (c) => {
  return c.text('Hello from Hono with middleware!')
})

app.get('/api/public', (c) => {
  return c.json({ message: 'This is a public endpoint' })
})

app.get('/protected/data', (c) => {
  return c.json({ message: 'This is a protected endpoint', user: 'authenticated' })
})

// 2. Custom middleware
const app = new Hono()

// Request timing middleware
const timingMiddleware = async (c: any, next: any) => {
  const start = Date.now()
  await next()
  const duration = Date.now() - start
  c.header('X-Response-Time', `${duration}ms`)
  console.log(`${c.req.method} ${c.req.path} - ${duration}ms`)
}

// Request ID middleware
const requestIdMiddleware = async (c: any, next: any) => {
  const id = crypto.randomUUID()
  c.header('X-Request-ID', id)
  c.set('requestId', id)
  await next()
}

// Rate limiting middleware
const rateLimitMap = new Map()
const rateLimitMiddleware = async (c: any, next: any) => {
  const ip = c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown'
  const now = Date.now()
  const windowMs = 60000 // 1 minute
  const maxRequests = 10

  const record = rateLimitMap.get(ip)

  if (!record || now > record.resetTime) {
    rateLimitMap.set(ip, { count: 1, resetTime: now + windowMs })
    await next()
    return
  }

  if (record.count >= maxRequests) {
    c.status(429)
    return c.json({ error: 'Too Many Requests' })
  }

  record.count++
  await next()
}

// Apply middleware
app.use('*', timingMiddleware)
app.use('*', requestIdMiddleware)
app.use('/api/*', rateLimitMiddleware)

// Routes
app.get('/', (c) => {
  const requestId = c.get('requestId')
  return c.json({ message: 'Hello!', requestId })
})

app.get('/api/data', (c) => {
  return c.json({ data: 'Some API data', timestamp: new Date().toISOString() })
})

// 3. Authentication middleware with JWT
const app = new Hono()
const JWT_SECRET = 'your-jwt-secret'

// JWT middleware
app.use('/api/*', jwt({
  secret: JWT_SECRET,
  alg: 'HS256'
}))

// Mock user middleware
const userMiddleware = async (c: any, next: any) => {
  const payload = c.get('jwtPayload')

  // Mock user lookup
  const user = {
    id: payload.sub,
    name: 'John Doe',
    email: '[email protected]',
    role: payload.role || 'user'
  }

  c.set('user', user)
  await next()
}

// Apply user middleware to protected routes
app.use('/api/protected/*', userMiddleware)

// Public routes
app.get('/', (c) => {
  return c.text('Public endpoint')
})

app.post('/auth/login', async (c) => {
  const { username, password } = await c.req.json()

  // Mock authentication
  if (username === 'admin' && password === 'password') {
    const token = await jwt.sign({
      sub: '1234567890',
      name: 'John Doe',
      role: 'admin'
    }, JWT_SECRET)

    return c.json({ token })
  }

  c.status(401)
  return c.json({ error: 'Invalid credentials' })
})

// Protected routes
app.get('/api/protected/profile', (c) => {
  const user = c.get('user')
  return c.json({ user })
})

app.get('/api/protected/admin', (c) => {
  const user = c.get('user')

  if (user.role !== 'admin') {
    c.status(403)
    return c.json({ error: 'Admin access required' })
  }

  return c.json({ message: 'Admin data', stats: { users: 1000, posts: 5000 } })
})

// 4. Validation middleware
const app = new Hono()

// Generic validation middleware
const validateBody = (schema: any) => {
  return async (c: any, next: any) => {
    try {
      const body = await c.req.json()
      const errors: string[] = []

      for (const [key, rules] of Object.entries(schema)) {
        const value = body[key]

        if (rules.required && (value === undefined || value === null)) {
          errors.push(`${key} is required`)
          continue
        }

        if (value !== undefined && rules.type && typeof value !== rules.type) {
          errors.push(`${key} must be of type ${rules.type}`)
        }

        if (rules.minLength && value && value.length < rules.minLength) {
          errors.push(`${key} must be at least ${rules.minLength} characters`)
        }

        if (rules.pattern && value && !rules.pattern.test(value)) {
          errors.push(`${key} format is invalid`)
        }
      }

      if (errors.length > 0) {
        c.status(400)
        return c.json({ errors })
      }

      c.set('validatedBody', body)
      await next()
    } catch (error) {
      c.status(400)
      return c.json({ error: 'Invalid JSON' })
    }
  }
}

// User creation schema
const userSchema = {
  name: { required: true, type: 'string', minLength: 2 },
  email: {
    required: true,
    type: 'string',
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  },
  age: { type: 'number', minimum: 18 }
}

// Routes with validation
app.post('/api/users', validateBody(userSchema), (c) => {
  const body = c.get('validatedBody')
  const user = {
    id: Date.now(),
    ...body,
    createdAt: new Date().toISOString()
  }

  return c.json({ message: 'User created', user })
})

// 5. Content negotiation middleware
const app = new Hono()

// Content type middleware
const contentTypeMiddleware = async (c: any, next: any) => {
  const accept = c.req.header('Accept') || ''

  if (accept.includes('application/json')) {
    c.set('responseType', 'json')
  } else if (accept.includes('text/html')) {
    c.set('responseType', 'html')
  } else {
    c.set('responseType', 'text')
  }

  await next()
}

app.use('*', contentTypeMiddleware)

app.get('/api/data', (c) => {
  const data = {
    message: 'Hello, World!',
    timestamp: new Date().toISOString(),
    data: [1, 2, 3, 4, 5]
  }

  const responseType = c.get('responseType')

  switch (responseType) {
    case 'json':
      return c.json(data)
    case 'html':
      const html = `
        <!DOCTYPE html>
        <html>
        <head><title>Data</title></head>
        <body>
          <h1>${data.message}</h1>
          <p>Timestamp: ${data.timestamp}</p>
          <ul>${data.data.map((item: number) => `<li>${item}</li>`).join('')}</ul>
        </body>
        </html>
      `
      return c.html(html)
    default:
      return c.text(`Message: ${data.message}, Timestamp: ${data.timestamp}`)
  }
})

// 6. Caching middleware
const app = new Hono()
const cache = new Map()

const cacheMiddleware = (ttl: number = 60000) => {
  return async (c: any, next: any) => {
    const key = c.req.url
    const cached = cache.get(key)

    if (cached && Date.now() - cached.timestamp < ttl) {
      c.header('X-Cache', 'HIT')
      return new Response(cached.response)
    }

    await next()

    const response = c.res.clone()
    cache.set(key, {
      response,
      timestamp: Date.now()
    })

    c.header('X-Cache', 'MISS')
  }
}

app.get('/api/slow-data', cacheMiddleware(5000), async (c) => {
  // Simulate slow operation
  await new Promise(resolve => setTimeout(resolve, 1000))

  return c.json({
    data: 'This data was slow to generate',
    timestamp: new Date().toISOString()
  })
})

// 7. Database middleware
const app = new Hono()

// Mock database
const db = {
  users: [
    { id: 1, name: 'John Doe', email: '[email protected]' },
    { id: 2, name: 'Jane Smith', email: '[email protected]' }
  ]
}

// Database middleware
const dbMiddleware = async (c: any, next: any) => {
  c.set('db', db)
  await next()
}

app.use('/api/*', dbMiddleware)

app.get('/api/users', (c) => {
  const db = c.get('db')
  return c.json(db.users)
})

app.get('/api/users/:id', (c) => {
  const db = c.get('db')
  const id = parseInt(c.req.param('id'))
  const user = db.users.find((u: any) => u.id === id)

  if (!user) {
    c.status(404)
    return c.json({ error: 'User not found' })
  }

  return c.json(user)
})

// 8. Composed middleware example
const app = new Hono()

// Create composed middleware
const apiMiddleware = [
  logger(),
  cors({ origin: '*' }),
  timingMiddleware,
  requestIdMiddleware
]

// Apply multiple middleware
app.use('/api/*', ...apiMiddleware)

app.get('/api/test', (c) => {
  const requestId = c.get('requestId')
  return c.json({
    message: 'API endpoint with composed middleware',
    requestId,
    timestamp: new Date().toISOString()
  })
})

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

💻 Hono 高级路由 typescript

🟡 intermediate

高级路由示例,包括嵌套路由、路由组和参数验证

// Hono Advanced Routing Examples

import { Hono } from 'hono'
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'

// 1. Nested routes and route groups
const app = new Hono()

// User routes group
const userRoutes = new Hono()

userRoutes.get('/', (c) => {
  return c.json([
    { id: 1, name: 'John Doe', email: '[email protected]' },
    { id: 2, name: 'Jane Smith', email: '[email protected]' }
  ])
})

userRoutes.get('/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: `User ${id}`, email: `user${id}@example.com` })
})

userRoutes.post('/', async (c) => {
  const body = await c.req.json()
  return c.json({ id: Date.now(), ...body, createdAt: new Date().toISOString() })
})

userRoutes.put('/:id', async (c) => {
  const id = c.req.param('id')
  const body = await c.req.json()
  return c.json({ id, ...body, updatedAt: new Date().toISOString() })
})

userRoutes.delete('/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ message: `User ${id} deleted` })
})

// Post routes group
const postRoutes = new Hono()

postRoutes.get('/', (c) => {
  return c.json([
    { id: 1, title: 'First Post', content: 'Hello, World!' },
    { id: 2, title: 'Second Post', content: 'Another post' }
  ])
})

postRoutes.get('/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, title: `Post ${id}`, content: `Content for post ${id}` })
})

// Mount route groups
app.route('/users', userRoutes)
app.route('/posts', postRoutes)

// 2. Route with parameter validation using Zod
const app = new Hono()

// Define validation schemas
const userSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().min(18).optional()
})

const updateUserSchema = z.object({
  name: z.string().min(2).optional(),
  email: z.string().email().optional(),
  age: z.number().min(18).optional()
})

// Routes with validation
app.post('/users', zValidator('json', userSchema), (c) => {
  const validatedData = c.req.valid('json')
  return c.json({
    message: 'User created successfully',
    user: {
      id: Date.now(),
      ...validatedData,
      createdAt: new Date().toISOString()
    }
  })
})

app.put('/users/:id',
  zValidator('param', z.object({ id: z.string().regex(/^\d+$/) })),
  zValidator('json', updateUserSchema),
  (c) => {
    const { id } = c.req.valid('param')
    const updateData = c.req.valid('json')

    return c.json({
      message: `User ${id} updated successfully`,
      user: {
        id: parseInt(id),
        ...updateData,
        updatedAt: new Date().toISOString()
      }
    })
  }
)

// 3. Advanced route matching
const app = new Hono()

// Route with optional parameters
app.get('/users/:id/posts/:postId?', (c) => {
  const userId = c.req.param('id')
  const postId = c.req.param('postId')

  if (postId) {
    return c.json({ userId, postId, message: `Specific post ${postId} for user ${userId}` })
  }

  return c.json({ userId, message: `All posts for user ${userId}` })
})

// Route with wildcards
app.get('/static/*', (c) => {
  const path = c.req.param('*')
  return c.json({ path, message: `Serving static file: ${path}` })
})

// Route with multiple HTTP methods
app.on(['GET', 'POST'], '/webhook', (c) => {
  if (c.req.method === 'GET') {
    return c.json({ message: 'Webhook endpoint - GET method' })
  }

  if (c.req.method === 'POST') {
    return c.json({ message: 'Webhook endpoint - POST method', received: true })
  }
})

// Custom route handler
app.all('/health', (c) => {
  const status = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    method: c.req.method,
    uptime: process.uptime?.() || 0
  }

  return c.json(status)
})

// 4. Route guards and middleware
const app = new Hono()

// Authentication guard
const authGuard = async (c: any, next: any) => {
  const auth = c.req.header('Authorization')

  if (!auth?.startsWith('Bearer ')) {
    c.status(401)
    return c.json({ error: 'No token provided' })
  }

  // Mock token validation
  const token = auth.slice(7)
  if (token !== 'valid-token') {
    c.status(401)
    return c.json({ error: 'Invalid token' })
  }

  c.set('user', { id: 1, name: 'John Doe', role: 'user' })
  await next()
}

// Admin guard
const adminGuard = async (c: any, next: any) => {
  const user = c.get('user')

  if (user?.role !== 'admin') {
    c.status(403)
    return c.json({ error: 'Admin access required' })
  }

  await next()
}

// Apply guards to routes
app.get('/public', (c) => {
  return c.json({ message: 'Public endpoint' })
})

app.get('/protected', authGuard, (c) => {
  const user = c.get('user')
  return c.json({ message: 'Protected endpoint', user })
})

app.get('/admin', authGuard, adminGuard, (c) => {
  return c.json({ message: 'Admin endpoint', data: 'Sensitive admin data' })
})

// 5. Route versioning
const app = new Hono()

// v1 API routes
const v1Routes = new Hono()

v1Routes.get('/users', (c) => {
  return c.json({
    version: 'v1',
    data: [
      { id: 1, name: 'John Doe', email: '[email protected]' }
    ]
  })
})

v1Routes.post('/users', async (c) => {
  const body = await c.req.json()
  return c.json({
    version: 'v1',
    message: 'User created',
    user: {
      id: Date.now(),
      name: body.name,
      email: body.email
    }
  })
})

// v2 API routes
const v2Routes = new Hono()

v2Routes.get('/users', (c) => {
  return c.json({
    version: 'v2',
    data: [
      { id: 1, name: 'John Doe', email: '[email protected]', profile: { age: 30 } },
      { id: 2, name: 'Jane Smith', email: '[email protected]', profile: { age: 25 } }
    ],
    metadata: {
      total: 2,
      page: 1,
      limit: 10
    }
  })
})

v2Routes.post('/users', zValidator('json', z.object({
  name: z.string().min(2),
  email: z.string().email(),
  profile: z.object({
    age: z.number().min(18),
    bio: z.string().optional()
  })
})), (c) => {
  const body = c.req.valid('json')
  return c.json({
    version: 'v2',
    message: 'User created',
    user: {
      id: Date.now(),
      ...body,
      createdAt: new Date().toISOString()
    }
  })
})

// Mount versioned routes
app.route('/api/v1', v1Routes)
app.route('/api/v2', v2Routes)

// Default API route (redirects to latest version)
app.get('/api/users', (c) => {
  return c.redirect('/api/v2/users')
})

// 6. Dynamic route handling
const app = new Hono()

// Database mock
const resources = new Map([
  ['users', [
    { id: 1, name: 'John Doe', email: '[email protected]' },
    { id: 2, name: 'Jane Smith', email: '[email protected]' }
  ]],
  ['posts', [
    { id: 1, title: 'First Post', content: 'Hello, World!' },
    { id: 2, title: 'Second Post', content: 'Another post' }
  ]],
  ['comments', [
    { id: 1, postId: 1, text: 'Great post!' },
    { id: 2, postId: 1, text: 'Thanks for sharing!' }
  ]]
])

// Dynamic resource routes
app.get('/:resource', (c) => {
  const resource = c.req.param('resource')
  const data = resources.get(resource)

  if (!data) {
    c.status(404)
    return c.json({ error: 'Resource not found' })
  }

  return c.json({ resource, data })
})

app.get('/:resource/:id', (c) => {
  const resource = c.req.param('resource')
  const id = parseInt(c.req.param('id'))
  const data = resources.get(resource)

  if (!data) {
    c.status(404)
    return c.json({ error: 'Resource not found' })
  }

  const item = data.find((item: any) => item.id === id)

  if (!item) {
    c.status(404)
    return c.json({ error: 'Item not found' })
  }

  return c.json({ resource, item })
})

// 7. Route with query parameters and filtering
const app = new Hono()

// Advanced filtering
app.get('/api/products', (c) => {
  const query = c.req.query()
  const {
    page = '1',
    limit = '10',
    search,
    category,
    sort = 'name',
    order = 'asc'
  } = query

  // Mock products
  const products = [
    { id: 1, name: 'Laptop', category: 'electronics', price: 999 },
    { id: 2, name: 'Book', category: 'books', price: 20 },
    { id: 3, name: 'Phone', category: 'electronics', price: 699 },
    { id: 4, name: 'Novel', category: 'books', price: 15 },
    { id: 5, name: 'Tablet', category: 'electronics', price: 499 }
  ]

  // Filter products
  let filteredProducts = products

  if (search) {
    filteredProducts = filteredProducts.filter(p =>
      p.name.toLowerCase().includes(search.toLowerCase())
    )
  }

  if (category) {
    filteredProducts = filteredProducts.filter(p =>
      p.category === category
    )
  }

  // Sort products
  filteredProducts.sort((a, b) => {
    const aVal = a[sort as keyof typeof a]
    const bVal = b[sort as keyof typeof b]

    if (order === 'desc') {
      return aVal > bVal ? -1 : 1
    }

    return aVal > bVal ? 1 : -1
  })

  // Paginate
  const pageNum = parseInt(page)
  const limitNum = parseInt(limit)
  const start = (pageNum - 1) * limitNum
  const paginatedProducts = filteredProducts.slice(start, start + limitNum)

  return c.json({
    products: paginatedProducts,
    pagination: {
      page: pageNum,
      limit: limitNum,
      total: filteredProducts.length,
      pages: Math.ceil(filteredProducts.length / limitNum)
    },
    filters: { search, category, sort, order }
  })
})

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

💻 Hono 部署 typescript

🔴 complex

各种平台的部署示例,包括Node.js、Deno、Bun和Cloudflare Workers

// Hono Deployment Examples

import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.json({
    message: 'Hello from Hono!',
    timestamp: new Date().toISOString(),
    runtime: getRuntimeInfo()
  })
})

function getRuntimeInfo() {
  if (typeof Deno !== 'undefined') {
    return 'Deno'
  } else if (typeof Bun !== 'undefined') {
    return 'Bun'
  } else if (typeof process !== 'undefined' && process.versions?.node) {
    return 'Node.js'
  } else {
    return 'Unknown'
  }
}

export default app

// 1. Node.js Deployment (package.json)
/*
{
  "name": "hono-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "start": "node dist/index.js",
    "build": "tsc",
    "preview": "node dist/index.js"
  },
  "dependencies": {
    "hono": "^4.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0"
  }
}
*/

// 2. Node.js entry point (src/index.ts)
import app from './app.js'

const port = process.env.PORT || 3000

console.log(`Server starting on port ${port}...`)
console.log(`Runtime: ${getRuntimeInfo()}`)

export default {
  fetch: app.fetch,
  port,
  hostname: '0.0.0.0'
}

// Alternative Node.js with Express adapter
/*
import { serve } from '@hono/node-server'
import app from './app.js'

const port = process.env.PORT || 3000

console.log(`Server is running on port ${port}`)

serve({
  fetch: app.fetch,
  port
})
*/

// 3. Deno Deployment (deno.json)
/*
{
  "compilerOptions": {
    "allowJs": true,
    "lib": ["deno.window"],
    "strict": true
  },
  "imports": {
    "hono": "https://deno.land/x/hono/mod.ts"
  },
  "tasks": {
    "dev": "deno run --allow-net --watch src/index.ts",
    "start": "deno run --allow-net src/index.ts"
  }
}
*/

// Deno entry point
/*
import app from './app.ts'

const port = parseInt(Deno.env.get('PORT') || '3000')

console.log(`Server starting on port ${port}...`)
console.log(`Runtime: ${Deno.version.deno}`)

Deno.serve({ port }, app.fetch)
*/

// 4. Bun Deployment
// bun.lockb - automatically generated
// package.json can be the same as Node.js

// Bun entry point
/*
import app from './app.ts'

const port = process.env.PORT || 3000

console.log(`Server starting on port ${port}...`)
console.log(`Runtime: Bun ${Bun.version}`)

export default {
  fetch: app.fetch,
  port,
  hostname: '0.0.0.0'
}
*/

// Or with Bun's built-in server:
/*
const server = Bun.serve({
  port: process.env.PORT || 3000,
  hostname: '0.0.0.0',
  fetch: app.fetch,
})

console.log(`Server running on ${server.hostname}:${server.port}`)
console.log(`Runtime: Bun ${Bun.version}`)
*/

// 5. Cloudflare Workers Deployment
// wrangler.toml
/*
name = "hono-workers"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[env.production]
name = "hono-workers-prod"

[[env.production.vars]]
ENVIRONMENT = "production"
*/

// Cloudflare Workers entry point
/*
import app from './app.ts'

export default {
  fetch: app.fetch,
}

// Named exports for Cloudflare Pages Functions
export const onRequest = app.fetch
*/

// 6. Vercel Deployment
// api/index.ts (for Vercel Functions)
/*
import app from './app.js'

export default app
*/

// Or with specific runtime configuration:
// api/hello.ts
/*
import { Hono } from 'hono'

const app = new Hono()

app.get('/hello', (c) => {
  return c.json({ message: 'Hello from Vercel!' })
})

export default app
*/

// 7. AWS Lambda Deployment
// lambda/index.ts
/*
import { Hono } from 'hono'
import { handle } from 'hono/aws-lambda'

const app = new Hono()

app.get('/', (c) => {
  return c.json({ message: 'Hello from AWS Lambda!' })
})

export const handler = handle(app)
*/

// 8. Netlify Functions Deployment
// netlify/functions/api.ts
/*
import { Hono } from 'hono'
import { serve } from '@hono/node-server'

const app = new Hono()

app.get('/', (c) => {
  return c.json({ message: 'Hello from Netlify Functions!' })
})

export default serve(app.fetch)
*/

// 9. Railway Deployment
// Use Dockerfile for Railway
/*
# Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]
*/

// 10. DigitalOcean App Platform
// Use same setup as Node.js with proper start script

// 11. Heroku Deployment
// Procfile
/*
web: npm start
*/

// package.json engines:
/*
{
  "engines": {
    "node": ">=18.0.0"
  }
}
*/

// 12. Environment-specific configurations
// config/environments.ts
/*
interface Config {
  port: number
  hostname: string
  env: string
  database?: {
    url: string
  }
}

function getConfig(): Config {
  const env = process.env.NODE_ENV || 'development'

  const configs: Record<string, Config> = {
    development: {
      port: parseInt(process.env.PORT || '3000'),
      hostname: 'localhost',
      env: 'development',
      database: {
        url: process.env.DEV_DB_URL || 'sqlite:dev.db'
      }
    },
    production: {
      port: parseInt(process.env.PORT || '8080'),
      hostname: '0.0.0.0',
      env: 'production',
      database: {
        url: process.env.DATABASE_URL || ''
      }
    },
    test: {
      port: parseInt(process.env.PORT || '3001'),
      hostname: 'localhost',
      env: 'test',
      database: {
        url: 'sqlite:test.db'
      }
    }
  }

  return configs[env] || configs.development
}

export const config = getConfig()
*/

// Using config in main file
/*
import app from './app.js'
import { config } from './config/environments.js'

console.log(`Starting server in ${config.env} mode`)
console.log(`Server running on ${config.hostname}:${config.port}`)

export default {
  fetch: app.fetch,
  port: config.port,
  hostname: config.hostname
}
*/

// 13. Docker deployment
// Dockerfile
/*
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

FROM node:18-alpine AS runner

WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

USER hono

EXPOSE 3000

ENV NODE_ENV=production

CMD ["node", "dist/index.js"]
*/

// docker-compose.yml
/*
version: '3.8'

services:
  hono-app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    restart: unless-stopped
*/

// 14. Health checks and readiness probes
// health.ts
/*
import { Hono } from 'hono'

const healthApp = new Hono()

healthApp.get('/health', (c) => {
  return c.json({
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime?.() || 0,
    version: process.env.npm_package_version || '1.0.0',
    environment: process.env.NODE_ENV || 'development'
  })
})

healthApp.get('/ready', (c) => {
  // Check dependencies
  const checks = {
    database: checkDatabaseConnection(),
    externalApi: checkExternalApi()
  }

  const allReady = Object.values(checks).every(Boolean)

  c.status(allReady ? 200 : 503)
  return c.json({
    ready: allReady,
    checks,
    timestamp: new Date().toISOString()
  })
})

function checkDatabaseConnection(): boolean {
  // Implement actual database health check
  return true
}

function checkExternalApi(): boolean {
  // Implement actual external API health check
  return true
}

export default healthApp
*/

// Integration with main app
/*
import app from './app.js'
import healthApp from './health.js'

// Mount health check routes
app.route('/', healthApp)

export default app
*/