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
*/