Hono Stacks
Hono 让简单的事情变得更简单,让困难的事情也变得简单。它不仅仅适合返回 JSON,还非常适合构建包括 REST API 服务端和客户端在内的全栈应用。
RPC
Hono 的 RPC 功能让你可以在几乎不改动代码的情况下共享 API 规范。由 hc 生成的客户端会读取这些规范,并以类型安全的方式访问端点。
以下库使这成为可能:
- Hono - API Server
- Zod - Validator
- Zod Validator Middleware
hc- HTTP Client
我们可以将这些组件的组合称为 Hono Stack。现在,让我们用它来创建一个 API 服务端和客户端。
编写 API
首先,编写一个接收 GET 请求并返回 JSON 的端点。
import { Hono } from 'hono'const app = new Hono()app.get('/hello', (c) => {return c.json({message: `Hello!`,})})
使用 Zod 验证
用 Zod 验证查询参数的值。

import { zValidator } from '@hono/zod-validator'import { z } from 'zod'app.get('/hello',zValidator('query',z.object({name: z.string(),})),(c) => {const { name } = c.req.valid('query')return c.json({message: `Hello! ${name}`,})})
共享类型
为了生成端点的类型规范,需要导出它的类型。
警告 要让 RPC 正确推断路由,所有包含的方法必须链式调用,并且端点或 app 的类型必须从声明的变量中推断。更多细节参见 Best Practices for RPC。
const route = app.get('/hello',zValidator('query',z.object({name: z.string(),})),(c) => {const { name } = c.req.valid('query')return c.json({message: `Hello! ${name}`,})})export type AppType = typeof route
客户端
接下来是客户端实现。通过将 AppType 类型作为泛型传入 hc 来创建客户端对象。这样智能补全功能就会自动生效,端点路径和请求类型会被提示。

import { AppType } from './server'import { hc } from 'hono/client'const client = hc<AppType>('/api')const res = await client.hello.$get({query: {name: 'Hono',},})
Response 与 fetch API 兼容,但 json() 获取的数据具有类型定义。

const data = await res.json()console.log(`${data.message}`)
共享 API 规范意味着你可以随时感知服务端的变化。

搭配 React
你可以使用 React 在 Cloudflare Pages 上构建应用。
API 服务端:
// functions/api/[[route]].tsimport { Hono } from 'hono'import { handle } from 'hono/cloudflare-pages'import { z } from 'zod'import { zValidator } from '@hono/zod-validator'const app = new Hono()const schema = z.object({id: z.string(),title: z.string(),})type Todo = z.infer<typeof schema>const todos: Todo[] = []const route = app.post('/todo', zValidator('form', schema), (c) => {const todo = c.req.valid('form')todos.push(todo)return c.json({message: 'created!',})}).get((c) => {return c.json({todos,})})export type AppType = typeof routeexport const onRequest = handle(app, '/api')
使用 React 和 React Query 的客户端:
// src/App.tsximport {useQuery,useMutation,QueryClient,QueryClientProvider,} from '@tanstack/react-query'import { AppType } from '../functions/api/[[route]]'import { hc, InferResponseType, InferRequestType } from 'hono/client'const queryClient = new QueryClient()const client = hc<AppType>('/api')export default function App() {return (<QueryClientProvider client={queryClient}><Todos /></QueryClientProvider>)}const Todos = () => {const query = useQuery({queryKey: ['todos'],queryFn: async () => {const res = await client.todo.$get()return await res.json()},})const $post = client.todo.$postconst mutation = useMutation<InferResponseType<typeof $post>,Error,InferRequestType<typeof $post>['form']>({mutationFn: async (todo) => {const res = await $post({form: todo,})return await res.json()},onSuccess: async () => {queryClient.invalidateQueries({ queryKey: ['todos'] })},onError: (error) => {console.log(error)},})return (<div><buttononClick={() => {mutation.mutate({id: Date.now().toString(),title: 'Write code',})}}>Add Todo</button><ul>{query.data?.todos.map((todo) => (<li key={todo.id}>{todo.title}</li>))}</ul></div>)}
