# JSON API 实战

URL: https://caijiao.org/hono/06-practice/01-json-api
Source: docs/hono/06-practice/01-json-api.md
Description: 使用 Hono 编写一个简单的 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 或其他验证器检查输入。
- 增加分页、排序和筛选。
- 增加鉴权和权限控制。
- 为接口补充测试。
