Fastify 简介

Fastify 是一个高性能的 Node.js Web 框架,专注于提供卓越的性能和开发体验。它由 Matteo Collina 创立,设计目标是成为最快的 Web 框架之一,同时保持开发的简洁性和灵活性。

核心特点

  • 高性能:Fastify 是目前最快的 Node.js Web 框架之一,处理请求的速度非常快。
  • 低开销:框架本身的开销很小,意味着更多的资源可以用于处理业务逻辑。
  • 插件系统:提供了强大的插件系统,便于扩展功能。
  • 请求验证:内置请求验证功能,确保数据的正确性和安全性。
  • 序列化:高效的响应序列化,减少网络传输时间。
  • 日志记录:内置结构化日志系统,便于调试和监控。
  • 类型支持:良好的 TypeScript 支持,提供类型安全。
  • 生态系统:丰富的官方和社区插件,满足各种需求。

安装与配置

安装 Fastify

使用 npm 或 yarn 安装 Fastify:

# 使用 npm 安装
npm install fastify

# 使用 yarn 安装
yarn add fastify

创建第一个 Fastify 应用

创建一个简单的 Fastify 应用,响应 HTTP 请求:

// app.js
const fastify = require('fastify')({ logger: true });

// 定义路由
fastify.get('/', async (request, reply) => {
  return { hello: 'world' };
});

// 启动服务器
const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
    fastify.log.info(`Server running at http://localhost:3000`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

运行应用:

node app.js

核心概念

路由

Fastify 的路由系统简洁而强大,支持多种 HTTP 方法和参数类型。

// 基本路由
fastify.get('/hello', async (request, reply) => {
  return { message: 'Hello World!' };
});

// 带参数的路由
fastify.get('/users/:id', async (request, reply) => {
  const { id } = request.params;
  return { userId: id };
});

// 带查询参数的路由
fastify.get('/search', async (request, reply) => {
  const { q } = request.query;
  return { query: q };
});

// POST 请求
fastify.post('/users', async (request, reply) => {
  const user = request.body;
  return { user };
});

请求验证

Fastify 内置了请求验证功能,使用 JSON Schema 验证请求数据:

// 定义验证模式
const userSchema = {
  type: 'object',
  required: ['name', 'email'],
  properties: {
    name: { type: 'string' },
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 18 }
  }
};

// 使用验证模式
fastify.post('/users', {
  schema: {
    body: userSchema
  }
}, async (request, reply) => {
  const user = request.body;
  return { user };
});

插件系统

Fastify 的插件系统是其核心特性之一,允许开发者模块化代码并扩展功能:

// 创建插件
const myPlugin = (fastify, options, done) => {
  fastify.get('/plugin', async (request, reply) => {
    return { plugin: 'Hello from plugin!' };
  });
  done();
};

// 注册插件
fastify.register(myPlugin);

// 带选项的插件
const pluginWithOptions = (fastify, options, done) => {
  fastify.get('/plugin-with-options', async (request, reply) => {
    return { message: options.message };
  });
  done();
};

// 注册带选项的插件
fastify.register(pluginWithOptions, { message: 'Hello from plugin with options!' });

中间件

Fastify 支持 Express 和 Connect 风格的中间件:

// 使用中间件
fastify.use((req, res, next) => {
  console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
  next();
});

// 路由级中间件
fastify.get('/protected', {
  preHandler: (request, reply, done) => {
    // 验证逻辑
    const token = request.headers.authorization;
    if (!token) {
      reply.status(401).send({ error: 'Unauthorized' });
      return;
    }
    done();
  }
}, async (request, reply) => {
  return { message: 'Protected route' };
});

钩子(Hooks)

Fastify 提供了多种生命周期钩子,用于在请求处理的不同阶段执行代码:

// 全局钩子
fastify.addHook('onRequest', (request, reply, done) => {
  console.log('Request received');
  done();
});

fastify.addHook('preHandler', async (request, reply) => {
  console.log('Before handling request');
});

fastify.addHook('onResponse', (request, reply, done) => {
  console.log('Response sent');
  done();
});

// 路由级钩子
fastify.get('/hooked', {
  preHandler: async (request, reply) => {
    console.log('Route-specific preHandler hook');
  }
}, async (request, reply) => {
  return { message: 'Hooked route' };
});

错误处理

Fastify 提供了统一的错误处理机制:

// 全局错误处理
fastify.setErrorHandler((error, request, reply) => {
  fastify.log.error(error);
  reply.status(500).send({ error: 'Internal Server Error' });
});

// 路由级错误处理
fastify.get('/error', async (request, reply) => {
  throw new Error('Something went wrong');
});

// 自定义错误
class CustomError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
  }
}

fastify.get('/custom-error', async (request, reply) => {
  throw new CustomError('Custom error message', 400);
});

// 处理自定义错误
fastify.setErrorHandler((error, request, reply) => {
  const statusCode = error.statusCode || 500;
  reply.status(statusCode).send({ error: error.message });
});

实用案例分析

构建 RESTful API

下面是一个使用 Fastify 构建 RESTful API 的示例,实现了基本的 CRUD 操作。

项目结构

├── app.js
├── routes/
│   └── users.js
├── services/
│   └── userService.js
└── package.json

代码实现

  1. 用户服务 (services/userService.js):
// 模拟用户数据
let users = [
  { id: 1, name: '张三', email: 'zhangsan@example.com' },
  { id: 2, name: '李四', email: 'lisi@example.com' }
];

// 获取所有用户
const getUsers = () => users;

// 根据 ID 获取用户
const getUserById = (id) => users.find(user => user.id === parseInt(id));

// 创建新用户
const createUser = (user) => {
  const newUser = {
    id: users.length + 1,
    ...user
  };
  users.push(newUser);
  return newUser;
};

// 更新用户
const updateUser = (id, user) => {
  const index = users.findIndex(user => user.id === parseInt(id));
  if (index === -1) return null;
  users[index] = { ...users[index], ...user };
  return users[index];
};

// 删除用户
const deleteUser = (id) => {
  const index = users.findIndex(user => user.id === parseInt(id));
  if (index === -1) return null;
  users.splice(index, 1);
  return { message: 'User deleted successfully' };
};

module.exports = {
  getUsers,
  getUserById,
  createUser,
  updateUser,
  deleteUser
};
  1. 用户路由 (routes/users.js):
const userService = require('../services/userService');

// 用户验证模式
const userSchema = {
  type: 'object',
  required: ['name', 'email'],
  properties: {
    name: { type: 'string' },
    email: { type: 'string', format: 'email' }
  }
};

// 更新用户验证模式
const updateUserSchema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    email: { type: 'string', format: 'email' }
  }
};

// 定义路由
const userRoutes = (fastify, options, done) => {
  // 获取所有用户
  fastify.get('/users', async (request, reply) => {
    const users = userService.getUsers();
    return users;
  });

  // 获取单个用户
  fastify.get('/users/:id', async (request, reply) => {
    const user = userService.getUserById(request.params.id);
    if (!user) {
      reply.status(404).send({ error: 'User not found' });
      return;
    }
    return user;
  });

  // 创建用户
  fastify.post('/users', {
    schema: {
      body: userSchema
    }
  }, async (request, reply) => {
    const newUser = userService.createUser(request.body);
    reply.status(201).send(newUser);
  });

  // 更新用户
  fastify.put('/users/:id', {
    schema: {
      body: updateUserSchema
    }
  }, async (request, reply) => {
    const updatedUser = userService.updateUser(request.params.id, request.body);
    if (!updatedUser) {
      reply.status(404).send({ error: 'User not found' });
      return;
    }
    return updatedUser;
  });

  // 删除用户
  fastify.delete('/users/:id', async (request, reply) => {
    const result = userService.deleteUser(request.params.id);
    if (!result) {
      reply.status(404).send({ error: 'User not found' });
      return;
    }
    reply.status(200).send(result);
  });

  done();
};

module.exports = userRoutes;
  1. 主应用 (app.js):
const fastify = require('fastify')({ logger: true });
const userRoutes = require('./routes/users');

// 注册路由插件
fastify.register(userRoutes);

// 健康检查路由
fastify.get('/health', async (request, reply) => {
  return { status: 'ok' };
});

// 全局错误处理
fastify.setErrorHandler((error, request, reply) => {
  fastify.log.error(error);
  const statusCode = error.statusCode || 500;
  reply.status(statusCode).send({ error: error.message || 'Internal Server Error' });
});

// 启动服务器
const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
    fastify.log.info(`Server running at http://localhost:3000`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

测试 API

使用 curl 或 Postman 测试 API:

# 获取所有用户
curl http://localhost:3000/users

# 获取单个用户
curl http://localhost:3000/users/1

# 创建用户
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name": "王五", "email": "wangwu@example.com"}'

# 更新用户
curl -X PUT http://localhost:3000/users/1 -H "Content-Type: application/json" -d '{"name": "张三更新", "email": "zhangsan-updated@example.com"}'

# 删除用户
curl -X DELETE http://localhost:3000/users/1

# 健康检查
curl http://localhost:3000/health

数据库集成

Fastify 支持多种数据库,包括 PostgreSQL、MySQL、MongoDB 等。下面以 PostgreSQL 为例,展示如何集成数据库。

安装依赖

# 安装 PostgreSQL 客户端
npm install pg

# 安装 Fastify PostgreSQL 插件
npm install @fastify/postgres

配置数据库连接

const fastify = require('fastify')({ logger: true });

// 注册 PostgreSQL 插件
fastify.register(require('@fastify/postgres'), {
  connectionString: 'postgres://username:password@localhost:5432/database'
});

// 使用数据库连接
fastify.get('/users', async (request, reply) => {
  const client = await fastify.pg.connect();
  try {
    const { rows } = await client.query('SELECT * FROM users');
    return rows;
  } finally {
    client.release();
  }
});

// 启动服务器
const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
    fastify.log.info(`Server running at http://localhost:3000`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

性能优化

Fastify 本身已经非常快,但还有一些方法可以进一步优化性能:

1. 使用 JSON Schema 验证

使用 JSON Schema 验证请求和响应可以提高性能,因为 Fastify 会编译 Schema 为高效的验证函数:

// 定义响应 Schema
const responseSchema = {
  type: 'object',
  properties: {
    id: { type: 'integer' },
    name: { type: 'string' },
    email: { type: 'string', format: 'email' }
  }
};

// 使用响应 Schema
fastify.get('/users/:id', {
  schema: {
    response: {
      200: responseSchema
    }
  }
}, async (request, reply) => {
  // 处理请求
});

2. 使用序列化钩子

对于复杂的响应对象,可以使用序列化钩子来优化序列化过程:

fastify.addHook('preSerialization', (request, reply, payload, done) => {
  // 优化序列化逻辑
  if (Array.isArray(payload)) {
    payload = payload.map(item => ({
      id: item.id,
      name: item.name,
      // 只包含必要的字段
    }));
  }
  done(null, payload);
});

3. 使用 Redis 作为缓存

对于频繁访问的数据,可以使用 Redis 作为缓存:

# 安装 Redis 客户端
npm install redis

# 安装 Fastify Redis 插件
npm install @fastify/redis
// 注册 Redis 插件
fastify.register(require('@fastify/redis'), {
  host: 'localhost',
  port: 6379
});

// 使用 Redis 缓存
fastify.get('/users/:id', async (request, reply) => {
  const { id } = request.params;
  const cacheKey = `user:${id}`;
  
  // 尝试从缓存获取
  const cachedUser = await fastify.redis.get(cacheKey);
  if (cachedUser) {
    return JSON.parse(cachedUser);
  }
  
  // 从数据库获取
  const user = await getUserFromDatabase(id);
  
  // 存入缓存
  await fastify.redis.set(cacheKey, JSON.stringify(user), 'EX', 3600);
  
  return user;
});

总结

Fastify 是一个高性能、低开销的 Node.js Web 框架,它提供了简洁而强大的 API,同时保持了卓越的性能。通过本教程,你应该已经了解了 Fastify 的核心概念和基本用法,包括路由、请求验证、插件系统、中间件、钩子和错误处理等。

Fastify 的插件系统和生态系统使其非常灵活,可以适应各种应用场景,从简单的 API 到复杂的微服务。它的高性能特性使其成为构建需要处理大量请求的应用的理想选择。

要深入学习 Fastify,建议查阅 官方文档 和实践更多的项目案例,以掌握其高级特性和最佳实践。Fastify 的社区也非常活跃,有大量的插件和资源可供参考。

« 上一篇 NestJS 教程 下一篇 » Koa 教程