Loading...
Loading...
Compare original and translation side by side
pnpm add honopnpm add honoconst app = new Hono()pnpm add zod @hono/zod-validatorconst app = new Hono()pnpm add zod @hono/zod-validatorimport { Hono } from 'hono';
import { serve } from '@hono/node-server';
const app = new Hono();
app.get('/', (c) => {
return c.text('Hello Hono!');
});
const port = 3000;
console.log(`Server is running on http://localhost:${port}`);
serve({
fetch: app.fetch,
port,
});import { Hono } from 'hono';
import { serve } from '@hono/node-server';
const app = new Hono();
app.get('/', (c) => {
return c.text('Hello Hono!');
});
const port = 3000;
console.log(`Server is running on http://localhost:${port}`);
serve({
fetch: app.fetch,
port,
});import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello from Bun!'));
export default {
port: 3000,
fetch: app.fetch,
};import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello from Bun!'));
export default {
port: 3000,
fetch: app.fetch,
};import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello from Cloudflare!'));
export default app;import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello from Cloudflare!'));
export default app;import { Hono } from 'hono';
const app = new Hono();
// Basic routes
app.get('/users', (c) => c.json({ users: [] }));
app.post('/users', (c) => c.json({ created: true }));
app.put('/users/:id', (c) => c.json({ updated: true }));
app.delete('/users/:id', (c) => c.json({ deleted: true }));
app.patch('/users/:id', (c) => c.json({ patched: true }));
// Multiple methods on same path
app.on(['GET', 'POST'], '/multi', (c) => {
return c.text(`Method: ${c.req.method}`);
});
// All methods
app.all('/catch-all', (c) => c.text('Any method'));import { Hono } from 'hono';
const app = new Hono();
// 基础路由
app.get('/users', (c) => c.json({ users: [] }));
app.post('/users', (c) => c.json({ created: true }));
app.put('/users/:id', (c) => c.json({ updated: true }));
app.delete('/users/:id', (c) => c.json({ deleted: true }));
app.patch('/users/:id', (c) => c.json({ patched: true }));
// 同一路径绑定多个方法
app.on(['GET', 'POST'], '/multi', (c) => {
return c.text(`Method: ${c.req.method}`);
});
// 匹配所有方法
app.all('/catch-all', (c) => c.text('Any method'));// Path parameters
app.get('/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ userId: id });
});
// Multiple parameters
app.get('/posts/:postId/comments/:commentId', (c) => {
const { postId, commentId } = c.req.param();
return c.json({ postId, commentId });
});
// Optional parameters
app.get('/users/:id?', (c) => {
const id = c.req.param('id');
return c.json({ userId: id ?? 'all' });
});// 路径参数
app.get('/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ userId: id });
});
// 多参数
app.get('/posts/:postId/comments/:commentId', (c) => {
const { postId, commentId } = c.req.param();
return c.json({ postId, commentId });
});
// 可选参数
app.get('/users/:id?', (c) => {
const id = c.req.param('id');
return c.json({ userId: id ?? 'all' });
});// Wildcard (matches anything after)
app.get('/files/*', (c) => {
return c.text('File handler');
});
// Regex patterns
app.get('/posts/:id{[0-9]+}', (c) => {
// Only matches numeric IDs
const id = c.req.param('id');
return c.json({ postId: Number.parseInt(id) });
});// 通配符(匹配路径后续所有内容)
app.get('/files/*', (c) => {
return c.text('File handler');
});
// 正则匹配
app.get('/posts/:id{[0-9]+}', (c) => {
// 仅匹配数字ID
const id = c.req.param('id');
return c.json({ postId: Number.parseInt(id) });
});import { Hono } from 'hono';
const app = new Hono();
// API v1 routes
const v1 = new Hono();
v1.get('/users', (c) => c.json({ version: 1, users: [] }));
v1.get('/posts', (c) => c.json({ version: 1, posts: [] }));
// API v2 routes
const v2 = new Hono();
v2.get('/users', (c) => c.json({ version: 2, users: [] }));
v2.get('/posts', (c) => c.json({ version: 2, posts: [] }));
// Mount route groups
app.route('/api/v1', v1);
app.route('/api/v2', v2);import { Hono } from 'hono';
const app = new Hono();
// API v1路由组
const v1 = new Hono();
v1.get('/users', (c) => c.json({ version: 1, users: [] }));
v1.get('/posts', (c) => c.json({ version: 1, posts: [] }));
// API v2路由组
const v2 = new Hono();
v2.get('/users', (c) => c.json({ version: 2, users: [] }));
v2.get('/posts', (c) => c.json({ version: 2, posts: [] }));
// 挂载路由组
app.route('/api/v1', v1);
app.route('/api/v2', v2);app.get('/search', (c) => {
// Single query param
const query = c.req.query('q');
// With default value
const page = c.req.query('page') ?? '1';
// All query params
const params = c.req.queries();
// { q: ['search'], page: ['1'], tags: ['a', 'b'] }
return c.json({ query, page, params });
});app.get('/search', (c) => {
// 单个查询参数
const query = c.req.query('q');
// 带默认值
const page = c.req.query('page') ?? '1';
// 所有查询参数
const params = c.req.queries();
// { q: ['search'], page: ['1'], tags: ['a', 'b'] }
return c.json({ query, page, params });
});// JSON body
app.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ received: body });
});
// Form data
app.post('/upload', async (c) => {
const formData = await c.req.formData();
const name = formData.get('name');
return c.text(`Received: ${name}`);
});
// Text body
app.post('/webhook', async (c) => {
const text = await c.req.text();
return c.text('OK');
});
// Parse once pattern
app.post('/data', async (c) => {
const body = await c.req.parseBody();
// Automatically detects JSON, form data, or multipart
return c.json(body);
});// JSON请求体
app.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ received: body });
});
// 表单数据
app.post('/upload', async (c) => {
const formData = await c.req.formData();
const name = formData.get('name');
return c.text(`Received: ${name}`);
});
// 文本请求体
app.post('/webhook', async (c) => {
const text = await c.req.text();
return c.text('OK');
});
// 自动解析模式
app.post('/data', async (c) => {
const body = await c.req.parseBody();
// 自动识别JSON、表单数据或多部分表单
return c.json(body);
});app.get('/headers', (c) => {
// Get single header
const auth = c.req.header('Authorization');
// Get all headers
const headers = c.req.raw.headers;
// Set response headers
c.header('X-Custom-Header', 'value');
c.header('Cache-Control', 'no-cache');
return c.json({ auth });
});app.get('/headers', (c) => {
// 获取单个请求头
const auth = c.req.header('Authorization');
// 获取所有请求头
const headers = c.req.raw.headers;
// 设置响应头
c.header('X-Custom-Header', 'value');
c.header('Cache-Control', 'no-cache');
return c.json({ auth });
});app.get('/users', (c) => {
return c.json({
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
],
});
});
// With status code
app.post('/users', (c) => {
return c.json({ created: true }, 201);
});
// Pretty print in development
app.get('/debug', (c) => {
return c.json({ data: 'value' }, 200, {
'Content-Type': 'application/json; charset=utf-8',
});
});app.get('/users', (c) => {
return c.json({
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
],
});
});
// 带状态码
app.post('/users', (c) => {
return c.json({ created: true }, 201);
});
// 开发环境格式化输出
app.get('/debug', (c) => {
return c.json({ data: 'value' }, 200, {
'Content-Type': 'application/json; charset=utf-8',
});
});// Text
app.get('/health', (c) => c.text('OK'));
// HTML
app.get('/home', (c) => {
return c.html('<h1>Welcome</h1>');
});
// Redirect
app.get('/old', (c) => c.redirect('/new'));
app.get('/external', (c) => c.redirect('https://example.com', 301));
// Stream
app.get('/stream', (c) => {
return c.stream(async (stream) => {
for (let i = 0; i < 10; i++) {
await stream.writeln(`Line ${i}`);
await new Promise((resolve) => setTimeout(resolve, 100));
}
});
});
// Not Found
app.get('/missing', (c) => c.notFound());// 文本响应
app.get('/health', (c) => c.text('OK'));
// HTML响应
app.get('/home', (c) => {
return c.html('<h1>Welcome</h1>');
});
// 重定向
app.get('/old', (c) => c.redirect('/new'));
app.get('/external', (c) => c.redirect('https://example.com', 301));
// 流式响应
app.get('/stream', (c) => {
return c.stream(async (stream) => {
for (let i = 0; i < 10; i++) {
await stream.writeln(`Line ${i}`);
await new Promise((resolve) => setTimeout(resolve, 100));
}
});
});
// 404响应
app.get('/missing', (c) => c.notFound());import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { prettyJSON } from 'hono/pretty-json';
import { secureHeaders } from 'hono/secure-headers';
const app = new Hono();
// Logger
app.use('*', logger());
// CORS
app.use('*', cors({
origin: ['http://localhost:3000', 'https://example.com'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
// Pretty JSON in development
if (process.env.NODE_ENV === 'development') {
app.use('*', prettyJSON());
}
// Security headers
app.use('*', secureHeaders());import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { prettyJSON } from 'hono/pretty-json';
import { secureHeaders } from 'hono/secure-headers';
const app = new Hono();
// 日志中间件
app.use('*', logger());
// CORS中间件
app.use('*', cors({
origin: ['http://localhost:3000', 'https://example.com'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
// 开发环境格式化JSON
if (process.env.NODE_ENV === 'development') {
app.use('*', prettyJSON());
}
// 安全响应头中间件
app.use('*', secureHeaders());// Simple middleware
app.use('*', async (c, next) => {
console.log(`[${c.req.method}] ${c.req.url}`);
await next();
});
// Auth middleware
const authMiddleware = async (c, next) => {
const token = c.req.header('Authorization');
if (!token) {
return c.json({ error: 'Unauthorized' }, 401);
}
// Verify token (example)
if (token !== 'Bearer valid-token') {
return c.json({ error: 'Invalid token' }, 403);
}
// Store user in context
c.set('user', { id: 1, name: 'Alice' });
await next();
};
// Apply to specific routes
app.use('/api/*', authMiddleware);
app.get('/api/profile', (c) => {
const user = c.get('user');
return c.json({ user });
});// 简单中间件
app.use('*', async (c, next) => {
console.log(`[${c.req.method}] ${c.req.url}`);
await next();
});
// 鉴权中间件
const authMiddleware = async (c, next) => {
const token = c.req.header('Authorization');
if (!token) {
return c.json({ error: 'Unauthorized' }, 401);
}
// 验证token(示例)
if (token !== 'Bearer valid-token') {
return c.json({ error: 'Invalid token' }, 403);
}
// 将用户信息存入上下文
c.set('user', { id: 1, name: 'Alice' });
await next();
};
// 应用到指定路由
app.use('/api/*', authMiddleware);
app.get('/api/profile', (c) => {
const user = c.get('user');
return c.json({ user });
});const timingMiddleware = async (c, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
c.header('X-Response-Time', `${ms}ms`);
};
app.use('*', timingMiddleware);const timingMiddleware = async (c, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
c.header('X-Response-Time', `${ms}ms`);
};
app.use('*', timingMiddleware);pnpm add zod @hono/zod-validatorpnpm add zod @hono/zod-validatorimport { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
// Define schemas
const userSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(120).optional(),
});
const idSchema = z.object({
id: z.string().regex(/^\d+$/),
});
const querySchema = z.object({
page: z.string().regex(/^\d+$/).default('1'),
limit: z.string().regex(/^\d+$/).default('10'),
});
// Validate request body
app.post('/users', zValidator('json', userSchema), async (c) => {
const user = c.req.valid('json');
// user is fully typed: { name: string; email: string; age?: number }
return c.json({ created: true, user }, 201);
});
// Validate path params
app.get('/users/:id', zValidator('param', idSchema), (c) => {
const { id } = c.req.valid('param');
return c.json({ userId: id });
});
// Validate query params
app.get('/users', zValidator('query', querySchema), (c) => {
const { page, limit } = c.req.valid('query');
return c.json({
page: Number.parseInt(page),
limit: Number.parseInt(limit),
users: [],
});
});import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
// 定义校验规则
const userSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(120).optional(),
});
const idSchema = z.object({
id: z.string().regex(/^\d+$/),
});
const querySchema = z.object({
page: z.string().regex(/^\d+$/).default('1'),
limit: z.string().regex(/^\d+$/).default('10'),
});
// 校验请求体
app.post('/users', zValidator('json', userSchema), async (c) => {
const user = c.req.valid('json');
// user类型完全安全:{ name: string; email: string; age?: number }
return c.json({ created: true, user }, 201);
});
// 校验路径参数
app.get('/users/:id', zValidator('param', idSchema), (c) => {
const { id } = c.req.valid('param');
return c.json({ userId: id });
});
// 校验查询参数
app.get('/users', zValidator('query', querySchema), (c) => {
const { page, limit } = c.req.valid('query');
return c.json({
page: Number.parseInt(page),
limit: Number.parseInt(limit),
users: [],
});
});import { zValidator } from '@hono/zod-validator';
app.post(
'/users',
zValidator('json', userSchema, (result, c) => {
if (!result.success) {
return c.json({
error: 'Validation failed',
details: result.error.flatten(),
}, 400);
}
}),
async (c) => {
const user = c.req.valid('json');
return c.json({ created: true, user }, 201);
}
);import { zValidator } from '@hono/zod-validator';
app.post(
'/users',
zValidator('json', userSchema, (result, c) => {
if (!result.success) {
return c.json({
error: 'Validation failed',
details: result.error.flatten(),
}, 400);
}
}),
async (c) => {
const user = c.req.valid('json');
return c.json({ created: true, user }, 201);
}
);app.get('/users/:id', async (c) => {
try {
const id = c.req.param('id');
const user = await db.users.findById(id);
if (!user) {
return c.json({ error: 'User not found' }, 404);
}
return c.json({ user });
} catch (error) {
console.error('Error fetching user:', error);
return c.json({ error: 'Internal server error' }, 500);
}
});app.get('/users/:id', async (c) => {
try {
const id = c.req.param('id');
const user = await db.users.findById(id);
if (!user) {
return c.json({ error: 'User not found' }, 404);
}
return c.json({ user });
} catch (error) {
console.error('Error fetching user:', error);
return c.json({ error: 'Internal server error' }, 500);
}
});import { HTTPException } from 'hono/http-exception';
app.onError((err, c) => {
console.error(`Error: ${err.message}`);
if (err instanceof HTTPException) {
return c.json({
error: err.message,
status: err.status,
}, err.status);
}
return c.json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
}, 500);
});
// Throw HTTP exceptions
app.get('/protected', (c) => {
throw new HTTPException(403, { message: 'Forbidden' });
});import { HTTPException } from 'hono/http-exception';
app.onError((err, c) => {
console.error(`Error: ${err.message}`);
if (err instanceof HTTPException) {
return c.json({
error: err.message,
status: err.status,
}, err.status);
}
return c.json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
}, 500);
});
// 抛出HTTP异常
app.get('/protected', (c) => {
throw new HTTPException(403, { message: 'Forbidden' });
});app.notFound((c) => {
return c.json({
error: 'Not Found',
path: c.req.path,
}, 404);
});app.notFound((c) => {
return c.json({
error: 'Not Found',
path: c.req.path,
}, 404);
});import type { Context } from 'hono';
type Variables = {
user: { id: number; name: string };
};
type Env = {
Variables: Variables;
};
const app = new Hono<Env>();
app.use('/api/*', async (c, next) => {
c.set('user', { id: 1, name: 'Alice' });
await next();
});
app.get('/api/profile', (c) => {
const user = c.get('user'); // Fully typed
return c.json({ user });
});import type { Context } from 'hono';
type Variables = {
user: { id: number; name: string };
};
type Env = {
Variables: Variables;
};
const app = new Hono<Env>();
app.use('/api/*', async (c, next) => {
c.set('user', { id: 1, name: 'Alice' });
await next();
});
app.get('/api/profile', (c) => {
const user = c.get('user'); // 类型完全安全
return c.json({ user });
});// server.ts
const app = new Hono()
.get('/posts', (c) => c.json({ posts: [] }))
.post('/posts', async (c) => {
const body = await c.req.json();
return c.json({ created: true, post: body }, 201);
});
export type AppType = typeof app;
// client.ts
import { hc } from 'hono/client';
import type { AppType } from './server';
const client = hc<AppType>('http://localhost:3000');
// Fully typed API calls
const res = await client.posts.$get();
const data = await res.json(); // { posts: [] }
await client.posts.$post({ json: { title: 'Hello' } });// server.ts
const app = new Hono()
.get('/posts', (c) => c.json({ posts: [] }))
.post('/posts', async (c) => {
const body = await c.req.json();
return c.json({ created: true, post: body }, 201);
});
export type AppType = typeof app;
// client.ts
import { hc } from 'hono/client';
import type { AppType } from './server';
const client = hc<AppType>('http://localhost:3000');
// 类型完全安全的API调用
const res = await client.posts.$get();
const data = await res.json(); // { posts: [] }
await client.posts.$post({ json: { title: 'Hello' } });import { serveStatic } from '@hono/node-server/serve-static';
// Serve from public directory
app.use('/static/*', serveStatic({ root: './' }));
// Serve index.html for SPA
app.get('*', serveStatic({ path: './dist/index.html' }));
// With custom 404
app.use('/assets/*', serveStatic({
root: './',
onNotFound: (path, c) => {
console.log(`${path} is not found`);
},
}));import { serveStatic } from '@hono/node-server/serve-static';
// 从public目录提供静态文件
app.use('/static/*', serveStatic({ root: './' }));
// 为单页应用提供index.html
app.get('*', serveStatic({ path: './dist/index.html' }));
// 自定义404处理
app.use('/assets/*', serveStatic({
root: './',
onNotFound: (path, c) => {
console.log(`${path} is not found`);
},
}));import { cors } from 'hono/cors';
// Permissive (development)
app.use('*', cors());
// Production config
app.use('/api/*', cors({
origin: (origin) => {
// Dynamic origin validation
return origin.endsWith('.example.com') ? origin : 'https://example.com';
},
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowHeaders: ['Content-Type', 'Authorization'],
exposeHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 600,
}));import { cors } from 'hono/cors';
// 宽松配置(开发环境)
app.use('*', cors());
// 生产环境配置
app.use('/api/*', cors({
origin: (origin) => {
// 动态验证源
return origin.endsWith('.example.com') ? origin : 'https://example.com';
},
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowHeaders: ['Content-Type', 'Authorization'],
exposeHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 600,
}));import { Hono } from 'hono';
type Bindings = {
DATABASE_URL: string;
API_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/config', (c) => {
// Access environment variables
const dbUrl = c.env.DATABASE_URL;
const apiKey = c.env.API_KEY;
return c.json({ configured: !!dbUrl && !!apiKey });
});
// Node.js
import { serve } from '@hono/node-server';
serve({
fetch: app.fetch,
port: 3000,
});
// Access via process.env in Node.js
const dbUrl = process.env.DATABASE_URL;import { Hono } from 'hono';
type Bindings = {
DATABASE_URL: string;
API_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/config', (c) => {
// 访问环境变量
const dbUrl = c.env.DATABASE_URL;
const apiKey = c.env.API_KEY;
return c.json({ configured: !!dbUrl && !!apiKey });
});
// Node.js环境
import { serve } from '@hono/node-server';
serve({
fetch: app.fetch,
port: 3000,
});
// 在Node.js中通过process.env访问
const dbUrl = process.env.DATABASE_URL;import { describe, it, expect } from 'vitest';
import { Hono } from 'hono';
describe('API Tests', () => {
const app = new Hono();
app.get('/hello', (c) => c.json({ message: 'Hello' }));
it('should return hello message', async () => {
const res = await app.request('/hello');
expect(res.status).toBe(200);
expect(await res.json()).toEqual({ message: 'Hello' });
});
it('should handle POST requests', async () => {
app.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ created: true, user: body }, 201);
});
const res = await app.request('/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice' }),
});
expect(res.status).toBe(201);
const data = await res.json();
expect(data.created).toBe(true);
expect(data.user.name).toBe('Alice');
});
});import { describe, it, expect } from 'vitest';
import { Hono } from 'hono';
describe('API Tests', () => {
const app = new Hono();
app.get('/hello', (c) => c.json({ message: 'Hello' }));
it('should return hello message', async () => {
const res = await app.request('/hello');
expect(res.status).toBe(200);
expect(await res.json()).toEqual({ message: 'Hello' });
});
it('should handle POST requests', async () => {
app.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ created: true, user: body }, 201);
});
const res = await app.request('/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice' }),
});
expect(res.status).toBe(201);
const data = await res.json();
expect(data.created).toBe(true);
expect(data.user.name).toBe('Alice');
});
});undefinedundefined
**Performance testing:**
```bash
**性能测试:**
```bash
**Validation testing:**
```bash
**校验测试:**
```bashundefinedundefined