教程 fastify tutorial

Schema 与类型联动技巧

在 Fastify 中,利用 JSON Schema 进行数据验证是一个常见的做法,而通过 TypeScript 与 JSON Schema 进行联动,可以进一步提升类型安全性和开发体验。通过正确的 Schema 与类型定义结合,开发者可以确保请求数据和响应数据的一致性,从而减少潜在的错误。

1. 使用 JSON Schema 定义请求和响应结构

Fastify 提供了内置的 JSON Schema 支持,可以用于定义请求体、查询字符串、路由参数和响应数据的结构。TypeScript 可以与这些 Schema 紧密结合,从而实现类型安全。

例子:定义请求体的 Schema 和类型

首先,我们可以使用 JSON Schema 来定义请求体的结构,并结合 TypeScript 类型来确保类型一致性。

typescript
import fastify from 'fastify';

const app = fastify();

// 定义请求体的 JSON Schema
const bodySchema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    age: { type: 'integer' }
  },
  required: ['name', 'age']
};

// TypeScript 类型定义
interface Greeting {
  name: string;
  age: number;
}

app.post('/greet', {
  schema: {
    body: bodySchema, // 关联 JSON Schema 到请求体
  }
}, async (request, reply) => {
  const { name, age }: Greeting = request.body; // 自动推导类型为 Greeting
  return { greeting: `Hello, ${name}, you are ${age} years old` };
});

app.listen(3000, (err, address) => {
  if (err) {
    app.log.error(err);
    process.exit(1);
  }
  app.log.info(`Server listening at ${address}`);
});

在这个例子中,我们首先定义了一个 JSON Schema bodySchema 来描述请求体的数据结构。然后,我们使用 TypeScript 的接口 Greeting 来定义请求体的类型。Fastify 会根据 bodySchema 来验证请求数据,TypeScript 会自动推导出 request.body 的类型为 Greeting,确保类型一致。

2. 使用 TypeScript 类型生成 JSON Schema

有时,我们可能需要反向操作:通过 TypeScript 类型生成 JSON Schema。我们可以使用第三方库如 typescript-json-schema 或 fastify-typegen 来自动生成与 TypeScript 类型关联的 JSON Schema。

例子:通过 TypeScript 类型生成 JSON Schema

假设我们有一个 TypeScript 类型 Greeting,并希望生成相应的 JSON Schema 来进行验证。

typescript
interface Greeting {
  name: string;
  age: number;
}

// 使用第三方工具生成 JSON Schema

你可以使用 typescript-json-schema 生成 JSON Schema:

bash
npx typescript-json-schema tsconfig.json Greeting --out greeting-schema.json

这将生成一个与 Greeting 类型相对应的 JSON Schema,之后可以将它用于 Fastify 的路由验证。

3. 联动技巧:提高开发效率

3.1 使用联合类型与 JSON Schema 进行协作

在处理复杂的数据结构时,TypeScript 的联合类型(Union Types)与 JSON Schema 配合使用可以大大提升灵活性和类型安全性。

typescript
const bodySchema = {
  type: 'object',
  properties: {
    status: { type: 'string', enum: ['active', 'inactive'] },
    data: { type: 'object' }
  },
  required: ['status', 'data']
};

// TypeScript 联合类型
type Status = 'active' | 'inactive';

interface RequestBody {
  status: Status;
  data: any;
}

在上面的例子中,status 字段的 JSON Schema 使用了枚举类型 enum,这与 TypeScript 中的联合类型 Status 保持一致。这样,开发者可以在编写代码时同时享受到类型检查和数据验证的好处。

3.2 结合类型和 Schema 进行响应验证

我们不仅可以使用 JSON Schema 来验证请求数据,还可以为响应数据定义 Schema,以确保响应数据符合预期结构。

typescript
const responseSchema = {
  type: 'object',
  properties: {
    greeting: { type: 'string' }
  },
  required: ['greeting']
};

interface GreetingResponse {
  greeting: string;
}

app.post('/greet', {
  schema: {
    body: bodySchema,   // 请求体 Schema
    response: {
      200: responseSchema  // 响应体 Schema
    }
  }
}, async (request, reply) => {
  const { name, age }: Greeting = request.body;
  const response: GreetingResponse = { greeting: `Hello, ${name}, you are ${age} years old` };
  return response;  // Fastify 会根据 responseSchema 验证返回值
});

在这个例子中,响应数据 GreetingResponse 会被 JSON Schema 验证,确保返回的数据符合预期格式。

4. 自动化 Schema 和类型同步

为了避免手动同步 TypeScript 类型和 JSON Schema,你可以使用一些工具来实现自动化同步。比如,fastify-typegen 插件可以自动生成和 TypeScript 类型关联的 JSON Schema。

安装 fastify-typegen

bash
npm install fastify-typegen --save-dev

配置和使用

typescript
import fastify from 'fastify';
import fastifyTypegen from 'fastify-typegen';

const app = fastify();

app.register(fastifyTypegen);

app.post('/greet', async (request, reply) => {
  const { name, age }: Greeting = request.body;
  return { greeting: `Hello, ${name}, you are ${age} years old` };
});

fastify-typegen 会自动生成 JSON Schema 和 TypeScript 类型之间的映射,避免了手动同步带来的麻烦。

5. 总结

通过结合 JSON Schema 和 TypeScript 类型,开发者可以实现更强的类型安全和自动化验证,确保请求和响应数据的结构符合预期。在 Fastify 中,类型与 Schema 的联动大大提升了开发体验,减少了开发过程中因类型不一致导致的错误。通过合理配置和使用这些技术,开发者可以构建更为稳定和健壮的应用程序。