# 构建一个简易 API 项目

URL: https://caijiao.org/fastify/demo/api-project
Source: docs/fastify/demo/api-project.md
Description: 教程将引导你如何构建一个简易的 API 项目，使用 Fastify 创建基本的 RESTful API。

在本教程中，我们将使用 [Fastify](/fastify/) 构建一个简单的 API 项目，展示如何创建基本的 RESTful API 路由、处理请求、返回响应，并进行简单的错误处理。我们将从安装 [Fastify](/fastify/) 开始，然后逐步构建完整的 API 项目。

## 1. 初始化项目

首先，我们需要创建一个新的项目文件夹，并初始化一个 Node.js 项目。

```bash
mkdir fastify-api
cd fastify-api
npm init -y
```

安装 Fastify：

```bash
npm install fastify
```

## 2. 创建 Fastify 应用

在项目根目录下，创建一个 `app.ts` 文件，并写入基本的 Fastify 配置和路由。

```typescript
import fastify from 'fastify';

const app = fastify();

// 创建根路由
app.get('/', async (request, reply) => {
  return { message: 'Welcome to the Fastify API' };
});

// 创建获取用户的 API 路由
app.get('/users', async (request, reply) => {
  return [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' }
  ];
});

// 创建获取单个用户的 API 路由
app.get('/users/:id', async (request, reply) => {
  const userId = request.params.id;
  // 简单的错误处理
  if (userId === '1') {
    return { id: 1, name: 'John Doe' };
  } else if (userId === '2') {
    return { id: 2, name: 'Jane Smith' };
  } else {
    return reply.status(404).send({ error: 'User not found' });
  }
});

// 启动 Fastify 服务器
const start = async () => {
  try {
    await app.listen(3000);
    console.log('Server listening on http://localhost:3000');
  } catch (err) {
    app.log.error(err);
    process.exit(1);
  }
};

start();
```

### 代码解释

- 我们使用 `fastify()` 创建了一个 Fastify 实例。
- 在 `app.get()` 中定义了三个路由：
  - 根路由 `/` 返回一个简单的欢迎消息。
  - `/users` 返回一个简单的用户列表。
  - `/users/:id` 返回指定 ID 的用户信息。如果用户 ID 不存在，则返回 404 错误。
  
## 3. 启动项目

接下来，运行以下命令启动 Fastify 服务器：

```bash
ts-node app.ts
```

如果你没有安装 `ts-node`，可以先执行：

```bash
npm install -g ts-node typescript
```

服务器成功启动后，你可以通过浏览器访问 `http://localhost:3000/`，看到返回的欢迎消息。访问 `http://localhost:3000/users` 会返回用户列表，而访问 `http://localhost:3000/users/1` 或 `http://localhost:3000/users/2` 将返回对应的用户信息。如果访问不存在的用户 ID（如 `http://localhost:3000/users/3`），将返回 `404` 错误。

## 4. 添加请求体处理

接下来，我们添加一个处理 POST 请求的路由，来接收数据并保存。我们将通过 `JSON` 格式的请求体来创建一个新的用户。

### 添加用户的路由

```typescript
app.post('/users', async (request, reply) => {
  const { name } = request.body as { name: string };
  if (!name) {
    return reply.status(400).send({ error: 'Name is required' });
  }
  // 模拟创建用户
  const newUser = { id: Date.now(), name };
  return reply.status(201).send(newUser);
});
```

### 代码解释

- 这个路由会接收一个包含用户姓名的请求体（JSON 格式）。
- 如果请求体中没有提供 `name` 字段，我们会返回 `400` 错误。
- 否则，创建一个新的用户并返回其信息，使用 `201` 状态码表示成功创建。

### 测试 POST 请求

你可以使用 Postman 或 `curl` 测试这个 POST 路由：

```bash
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name": "Alice"}'
```

响应将类似：

```json
{
  "id": 1630424343235,
  "name": "Alice"
}
```

## 5. 错误处理

在 Fastify 中，处理错误是非常重要的。我们可以通过 Fastify 提供的错误处理机制来定制化响应错误。

### 错误处理中间件

```typescript
app.setErrorHandler((error, request, reply) => {
  console.error(error);
  reply.status(500).send({ error: 'Internal Server Error' });
});
```

在上面的代码中，我们设置了一个全局的错误处理器，当发生任何未捕获的错误时，都会返回一个 `500` 状态码和错误信息。

## 6. 完整的代码

以下是包含所有内容的完整项目代码：

```typescript
import fastify from 'fastify';

const app = fastify();

// 根路由
app.get('/', async (request, reply) => {
  return { message: 'Welcome to the Fastify API' };
});

// 获取用户列表
app.get('/users', async (request, reply) => {
  return [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' }
  ];
});

// 获取单个用户
app.get('/users/:id', async (request, reply) => {
  const userId = request.params.id;
  if (userId === '1') {
    return { id: 1, name: 'John Doe' };
  } else if (userId === '2') {
    return { id: 2, name: 'Jane Smith' };
  } else {
    return reply.status(404).send({ error: 'User not found' });
  }
});

// 添加新用户
app.post('/users', async (request, reply) => {
  const { name } = request.body as { name: string };
  if (!name) {
    return reply.status(400).send({ error: 'Name is required' });
  }
  const newUser = { id: Date.now(), name };
  return reply.status(201).send(newUser);
});

// 错误处理
app.setErrorHandler((error, request, reply) => {
  console.error(error);
  reply.status(500).send({ error: 'Internal Server Error' });
});

// 启动服务器
const start = async () => {
  try {
    await app.listen(3000);
    console.log('Server listening on http://localhost:3000');
  } catch (err) {
    app.log.error(err);
    process.exit(1);
  }
};

start();
```

## 7. 总结

你已经构建了一个简易的 API 项目，包含基本的 GET 和 POST 路由，并且通过错误处理确保项目更加健壮。通过 Fastify，我们能够快速构建高效且易于维护的 API 服务。
