教程 hono tutorial

JSON API 实战

这一节实现一个内存版文章 API,用来串联路由、请求体、状态码和统一响应。

数据结构

ts
type Post = {
  id: string
  title: string
  content: string
  createdAt: string
}

const posts = new Map<string, Post>()

创建应用

ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/posts', (c) => {
  return c.json({
    data: Array.from(posts.values()),
  })
})

查询详情

ts
app.get('/posts/:id', (c) => {
  const post = posts.get(c.req.param('id'))

  if (!post) {
    return c.json({ message: 'Post not found' }, 404)
  }

  return c.json({ data: post })
})

创建文章

ts
app.post('/posts', async (c) => {
  const body = await c.req.json<{
    title?: string
    content?: string
  }>()

  if (!body.title || !body.content) {
    return c.json({ message: 'title and content are required' }, 400)
  }

  const post: Post = {
    id: crypto.randomUUID(),
    title: body.title,
    content: body.content,
    createdAt: new Date().toISOString(),
  }

  posts.set(post.id, post)

  return c.json({ data: post }, 201)
})

更新文章

ts
app.put('/posts/:id', async (c) => {
  const id = c.req.param('id')
  const post = posts.get(id)

  if (!post) {
    return c.json({ message: 'Post not found' }, 404)
  }

  const body = await c.req.json<{
    title?: string
    content?: string
  }>()

  const nextPost: Post = {
    ...post,
    title: body.title ?? post.title,
    content: body.content ?? post.content,
  }

  posts.set(id, nextPost)

  return c.json({ data: nextPost })
})

删除文章

ts
app.delete('/posts/:id', (c) => {
  const id = c.req.param('id')

  if (!posts.has(id)) {
    return c.json({ message: 'Post not found' }, 404)
  }

  posts.delete(id)

  return c.body(null, 204)
})

测试请求

bash
curl -X POST http://localhost:3000/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"Hono","content":"Hello API"}'

curl http://localhost:3000/posts

下一步改进

这个示例使用内存存储,重启后数据会丢失。真实项目需要:

  • 使用数据库保存数据。
  • 用 Zod 或其他验证器检查输入。
  • 增加分页、排序和筛选。
  • 增加鉴权和权限控制。
  • 为接口补充测试。