Schema设计模式:Schema-First与Code-First


Schema设计模式:Schema-First与Code-First

两种设计思路的根本分歧

Schema-First和Code-First是GraphQL服务端开发的两种路线,它们的分歧在于:先有Schema还是先有代码。

Schema-First(也叫Schema-Driven Design)的流程是:先编写SDL文件定义类型和操作,然后根据Schema编写解析器实现。Apollo Server和GraphQL Yoga是这种模式的代表。

Code-First(也叫Type-First)的流程是:先在代码中用装饰器或注解定义类型(比如TypeScript的类+装饰器),工具从代码自动生成SDL。TypeGraphQL(TypeScript)、TypeORM风格的框架、NestJS的GraphQL模块属于这个阵营。

Schema-First模式

Schema-First的工作流程:

  1. 团队(或前后端一起)用SDL定义Schema
  2. 生成TypeScript类型定义(用GraphQL Code Generator)
  3. 编写解析器实现
  4. 部署运行

一个典型的项目结构:

src/ schema/ user.graphql post.graphql comment.graphql common.graphql generated/ types.ts # 自动生成的TypeScript类型 resolvers/ user.resolver.ts post.resolver.ts comment.resolver.ts context.ts server.ts ​

Schema文件示例(post.graphql):

type Post implements Node & Timestamped { id: ID! title: String! content: String! status: PostStatus! author: User! tags: [Tag!]! comments(limit: Int = 10, offset: Int = 0): [Comment!]! createdAt: DateTime! updatedAt: DateTime! } input CreatePostInput { title: String! content: String! tags: [String!] = [] status: PostStatus = DRAFT } extend type Query { post(id: ID!): Post posts(filter: PostFilter, pagination: Pagination): PostConnection! } extend type Mutation { createPost(input: CreatePostInput!): Post! updatePost(id: ID!, input: UpdatePostInput!): Post! deletePost(id: ID!): Boolean! } extend type Subscription { postPublished: Post! } ​

解析器实现:

import { Resolvers } from '../generated/types'; export const postResolvers: Resolvers = { Query: { post: async (_, { id }, context) => { return context.dataSources.posts.getById(id); }, posts: async (_, { filter, pagination }, context) => { return context.dataSources.posts.getMany(filter, pagination); } }, Mutation: { createPost: async (_, { input }, context) => { // 验证权限 if (!context.user) throw new ForbiddenError('请先登录'); // 验证输入 if (input.title.length < 5) throw new UserInputError('标题至少5个字符'); // 创建 const post = await context.dataSources.posts.create({ ...input, authorId: context.user.id }); // 发布事件 await context.pubSub.publish('POST_PUBLISHED', { postPublished: post }); return post; } }, Post: { author: async (parent, _, context) => { return context.dataSources.users.getById(parent.authorId); }, comments: async (parent, { limit, offset }, context) => { return context.dataSources.comments.getByPostId(parent.id, limit, offset); }, tags: async (parent, _, context) => { return context.dataSources.tags.getByPostId(parent.id); } } }; ​

Schema-First的优势在于Schema是先行的、显式的——它在代码之前就确定了API的契约。这意味着前后端可以在Schema阶段就达成一致,前端可以先基于Schema开发(用mock数据),后端按Schema实现。Schema成为协作的中心文档。

Code-First模式

Code-First(以TypeGraphQL为例)在代码中直接定义类型:

import { Field, ObjectType, ID, InputType, Args, Query, Mutation, Resolver } from 'type-graphql'; @ObjectType() class Post { @Field(() => ID) id!: string; @Field() title!: string; @Field() content!: string; @Field(() => User) author!: Promise<User>; @Field(() => [Comment]) @Field(() => [Comment]) async comments(@Arg('limit', { defaultValue: 10 }) limit: number): Promise<Comment[]> { // ... } } @InputType() class CreatePostInput { @Field() title!: string; @Field() content!: string; @Field(() => [String], { defaultValue: [] }) tags!: string[]; } @Resolver(() => Post) class PostResolver { @Query(() => Post, { nullable: true }) async post(@Arg('id', () => ID) id: string): Promise<Post | null> { // ... } @Mutation(() => Post) async createPost(@Arg('input') input: CreatePostInput): Promise<Post> { // ... } } ​

Code-First的优势在于类型安全和开发效率——你只需要维护一套TypeScript代码,不需要在SDL和TypeScript之间保持同步。IDE的类型推断、重构、自动补全都能直接工作。

两种模式的对比

维度 Schema-First Code-First
契约定义位置 SDL文件(语言无关) 代码中的装饰器(语言绑定)
前后端协作 Schema先行,前端可以提前开发 需要先有代码才能生成Schema
类型安全 需要代码生成步骤 原生类型安全
多语言后端 Schema是共享契约,任何语言实现 每种语言各自生成Schema
学习曲线 需要学SDL语法 需要学框架的装饰器API
适合场景 大团队、多语言、API先行 TypeScript单栈团队、快速原型

在实际项目中,选择取决于团队的技术栈和协作模式。如果是TypeScript全栈团队、项目初期、快速迭代,Code-First更高效。如果是Java后端+React前端、或者多个后端服务需要共享Schema,Schema-First更合适。

还有一种混合模式值得了解:在TypeScript项目中用Schema-First定义Schema,然后用GraphQL Code Generator生成TypeScript类型,在解析器中享受类型安全的同时保持Schema的中心地位。这种模式在实践中很常见,也是我推荐的默认选择。

图:Schema-First与Code-First对比

图:Schema-First与Code-First对比

作者与出处
原作者:灏天文库
来源:平台策划编纂
整理人: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U