GraphQL Schema Design 最佳实践

GraphQL 是一种新型的数据查询语言,它能够使得前端开发人员更加便捷与灵活的进行数据查询。在 GraphQL 中,Schema 设计是非常重要的一项工作,好的 Schema 设计能够让我们更加方便的进行数据查询,而不好的 Schema 设计则会使得查询出现很多冗余,并且会对服务器性能造成很大压力。本文将围绕 GraphQL 的 Schema 设计为主题,介绍一些 Schema 设计的最佳实践。

1. 单一责任原则

在设计 GraphQL Schema 的时候,我们应该遵循单一责任原则,即一个 Query 类型只查询一类数据,一个 Mutation 类型只操作一类数据。这样做的好处是能够将资源隔离,使得 Schema 更加清晰明了。

举个例子,下面是一个查询文章和查询标签的 Schema 设计:

type Query {
  article(id: ID!): Article
  articlesByTag(tag: String!): [Article]
  tag(id: ID!): Tag
}

type Mutation {
  createArticle(input: ArticleInput!): Article
  createTag(input: TagInput!): Tag
}

type Article {
  id: ID!
  title: String!
  content: String!
  tags: [Tag]!
}

type Tag {
  id: ID!
  name: String!
  articles: [Article]!
}

input ArticleInput {
  title: String!
  content: String!
  tagIds: [ID!]!
}

input TagInput {
  name: String!
  articleIds: [ID!]!
}

上面的 Schema 设计中,我们将查询文章和查询标签分别放在了两个 Query 类型中,并为它们指定了不同的名称,这样能够清晰地区分开数据的来源。同时,我们定义了相应的 Mutation 类型用于新增文章和新增标签,而这两个 Mutation 类型的名称和 Query 类型的名称都不一样,这样也是符合单一责任原则的。

2. 嵌套结构

在 GraphQL Schema 设计中,我们经常会遇到嵌套结构的数据,如文章的评论、标签的子标签等等。在这种情况下,我们应该将数据的嵌套结构在 Schema 中展开,这样可以使得查询更加清晰明了。

举个例子,下面是一个包含嵌套结构的 Schema 设计:

type Query {
  book(id: ID!): Book
}

type Book {
  id: ID!
  name: String!
  author: Author!
  chapters: [Chapter]!
  comments: [Comment]!
}

type Chapter {
  id: ID!
  name: String!
  content: String!
}

type Comment {
  id: ID!
  content: String!
  book: Book!
}

type Author {
  id: ID!
  name: String!
  books: [Book]!
}

上面的 Schema 设计中,我们将图书的章节和评论分别放在了 Chapters 和 Comments 类型中,同时也分别定义了相应的类型用于表示章节和评论的详细信息。而在这些数据类型中,我们都通过字段来描述了各个数据之间的关系,比如在 Comment 类型中,我们通过字段 "book" 来描述一条评论所属的图书,这样使得查询数据时更加明确。

3. 输入类型

在开发 GraphQL API 时,我们经常需要从客户端获取数据,然后使用这些数据进行查询或者操作。为了方便从客户端获取数据,我们可以利用输入类型来让客户端更加方便的提交数据。

举个例子,下面我们来看一下 Comment 类型的新增接口:

type Mutation {
  createComment(input: CommentInput!): Comment
}

input CommentInput {
  content: String!
  bookId: ID!
}

在上面的代码中,我们定义了一个新的输入类型 CommentInput,该类型包含了客户端需要提交的数据,这样客户端就可以通过输入这些数据,然后使用 createComment 接口来新增评论了。

4. 抽象类型

在 GraphQL 中,我们可以使用接口(interface)和 Union 类型来定义抽象类型。抽象类型表示一个对象只包含某些共用的字段,而不关心对象的实际类型。

举个例子,下面是一个包含了接口的 Schema 设计:

interface IBlock {
  id: ID!
  content: String!
}

type TextBlock implements IBlock {
  id: ID!
  content: String!
  fontSize: Int!
}

type ImageBlock implements IBlock {
  id: ID!
  content: String!
  imageUrl: String!
}

type Query {
  block(id: ID!): IBlock
}

在上面的 Schema 设计中,我们定义了一个接口 IBlock,它包含了共用的字段 "id" 和 "content"。同时,我们还定义了两个类型 TextBlock 和 ImageBlock,这两个类型实现了 IBlock 接口,但又拥有各自特有的字段。在 Query 类型中,我们使用了 IBlock 接口来查询块数据,GraphQL 会根据实际类型的不同,自动选择 TextBlock 或者 ImageBlock 中包含的字段来返回查询结果。

5. 描述枚举

在 GraphQL 中,我们可以使用枚举类型来表示某个字段可能的取值。为了方便对枚举类型进行描述,我们可以使用描述符来为枚举类型添加便于理解的描述信息。

举个例子,下面是一个包含了枚举类型和描述符的 Schema 设计:

enum OrderDirection {
  ASC
  DESC
}

type Query {
  articles(limit: Int!, offset: Int!, order: OrderDirection!): [Article]
}

在上面的 Schema 设计中,我们定义了一个名为 "OrderDirection" 的枚举类型,它包含了两个取值 "ASC" 和 "DESC"。同时,我们在 Query 类型的 "order" 字段上使用了 "OrderDirection" 枚举类型,并为该枚举类型添加了描述信息 "文章排序的方向"。这样,客户端在使用 "order" 查询时,就能够直接使用 "ASC" 或者 "DESC" 来进行查询,而不需要了解后台定义的具体取值范围。

总结

本文主要介绍了 GraphQL Schema 设计的最佳实践,包括单一责任原则、嵌套结构、输入类型、抽象类型、描述枚举等方面。通过学习和使用这些最佳实践,我们可以更加清晰明了地设计和实现 GraphQL API,提高前端开发人员的开发效率和体验。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a3e35dadd4f0e0ffc134e8


纠错反馈