GraphQL Schema 定义 #

Schema 是什么? #

Schema 是 GraphQL API 的蓝图,它定义了 API 的类型系统、操作入口和数据结构。

text
┌─────────────────────────────────────────────────────────────┐
│                    Schema 的作用                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐                                            │
│  │   Schema    │  ← API 契约                                │
│  └──────┬──────┘                                            │
│         │                                                   │
│    ┌────┼────┬────────┐                                      │
│    ▼    ▼    ▼        ▼                                      │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐                                │
│ │类型│ │查询│ │变更│ │订阅│                                │
│ │定义│ │入口│ │入口│ │入口│                                │
│ └────┘ └────┘ └────┘ └────┘                                │
│                                                             │
│  作用:                                                     │
│  ✅ 定义数据模型                                            │
│  ✅ 定义操作入口                                            │
│  ✅ 生成文档                                                │
│  ✅ 验证查询                                                │
│  ✅ 类型检查                                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基本结构 #

根类型 #

GraphQL Schema 有三个根类型:

graphql
schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

Query 类型 #

定义所有查询操作:

graphql
type Query {
  users: [User!]!
  user(id: ID!): User
  posts(limit: Int, offset: Int): [Post!]!
  post(id: ID!): Post
  search(query: String!): [SearchResult!]!
}

Mutation 类型 #

定义所有变更操作:

graphql
type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): DeleteResult!
  
  createPost(input: CreatePostInput!): Post!
  updatePost(id: ID!, input: UpdatePostInput!): Post!
  deletePost(id: ID!): DeleteResult!
}

Subscription 类型 #

定义所有订阅操作:

graphql
type Subscription {
  onUserCreated: User!
  onUserUpdated(id: ID!): User!
  onPostCreated: Post!
  onCommentAdded(postId: ID!): Comment!
}

类型定义 #

对象类型 #

graphql
type User {
  id: ID!
  name: String!
  email: String!
  age: Int
  status: UserStatus!
  role: Role!
  posts: [Post!]!
  comments: [Comment!]!
  createdAt: DateTime!
  updatedAt: DateTime
}

字段说明 #

graphql
type Post {
  id: ID!
  title: String!
  content: String!
  excerpt: String
  status: PostStatus!
  author: User!
  comments(first: Int, after: String): CommentConnection!
  tags: [String!]!
  viewCount: Int!
  createdAt: DateTime!
  publishedAt: DateTime
}

字段参数 #

graphql
type Query {
  user(id: ID!): User
  users(
    filter: UserFilter
    orderBy: UserOrderBy
    first: Int
    after: String
  ): UserConnection!
  
  search(
    query: String!
    type: SearchType
    limit: Int = 10
  ): [SearchResult!]!
}

输入类型 #

输入类型用于传递复杂参数:

graphql
input CreateUserInput {
  name: String!
  email: String!
  password: String!
  age: Int
  role: Role = USER
}

input UpdateUserInput {
  name: String
  email: String
  age: Int
  status: UserStatus
}

input UserFilter {
  status: UserStatus
  role: Role
  age: IntRange
  name: StringFilter
}

input IntRange {
  gte: Int
  lte: Int
}

input StringFilter {
  eq: String
  contains: String
  startsWith: String
  endsWith: String
}

枚举类型 #

graphql
enum UserStatus {
  ACTIVE
  INACTIVE
  PENDING
  SUSPENDED
  DELETED
}

enum Role {
  ADMIN
  MODERATOR
  USER
  GUEST
}

enum PostStatus {
  DRAFT
  PUBLISHED
  ARCHIVED
  DELETED
}

enum SortOrder {
  ASC
  DESC
}

接口类型 #

接口定义一组公共字段:

graphql
interface Node {
  id: ID!
  createdAt: DateTime!
}

interface Content {
  id: ID!
  title: String!
  author: User!
  createdAt: DateTime!
}

type Post implements Node & Content {
  id: ID!
  title: String!
  content: String!
  author: User!
  createdAt: DateTime!
  tags: [String!]!
}

type Page implements Node & Content {
  id: ID!
  title: String!
  body: String!
  author: User!
  createdAt: DateTime!
  slug: String!
}

查询接口:

graphql
type Query {
  node(id: ID!): Node
  content(id: ID!): Content
}

使用内联片段:

graphql
query {
  content(id: "1") {
    id
    title
    author {
      name
    }
    ... on Post {
      tags
    }
    ... on Page {
      slug
    }
  }
}

联合类型 #

联合类型表示多种类型之一:

graphql
union SearchResult = User | Post | Comment

union Media = Image | Video | Audio

union Notification = UserNotification | SystemNotification | MessageNotification

查询联合类型:

graphql
query {
  search(query: "GraphQL") {
    ... on User {
      id
      name
      email
    }
    ... on Post {
      id
      title
      excerpt
    }
    ... on Comment {
      id
      content
    }
  }
}

连接类型 #

用于分页的标准类型:

graphql
type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  node: User!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

使用:

graphql
type Query {
  users(
    first: Int
    after: String
    last: Int
    before: String
    filter: UserFilter
    orderBy: UserOrderBy
  ): UserConnection!
}

负载类型 #

变更操作的返回类型:

graphql
type UserMutationPayload {
  user: User
  success: Boolean!
  message: String
  errors: [FieldError!]
}

type FieldError {
  field: String!
  message: String!
  code: String
}

type DeleteResult {
  success: Boolean!
  message: String
  deletedId: ID
}

使用:

graphql
type Mutation {
  createUser(input: CreateUserInput!): UserMutationPayload!
  updateUser(id: ID!, input: UpdateUserInput!): UserMutationPayload!
  deleteUser(id: ID!): DeleteResult!
}

指令定义 #

自定义指令:

graphql
directive @auth(requires: Role = USER) on FIELD_DEFINITION
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE
directive @validate(constraint: String!) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
directive @cache(ttl: Int!) on FIELD_DEFINITION
directive @rateLimit(limit: Int!, duration: Int!) on FIELD_DEFINITION

使用指令:

graphql
type Query {
  users: [User!]! @auth(requires: ADMIN)
  user(id: ID!): User @cache(ttl: 300)
}

type Mutation {
  createUser(input: CreateUserInput!): User! @rateLimit(limit: 10, duration: 60)
}

type User {
  id: ID!
  name: String!
  email: String! @deprecated(reason: "Use `emails` field instead")
  emails: [Email!]!
}

模块化 Schema #

大型项目需要模块化组织:

文件结构 #

text
schema/
├── index.graphql
├── types/
│   ├── user.graphql
│   ├── post.graphql
│   ├── comment.graphql
│   └── common.graphql
├── inputs/
│   ├── user.inputs.graphql
│   ├── post.inputs.graphql
│   └── filter.inputs.graphql
├── enums/
│   ├── status.graphql
│   └── role.graphql
└── directives/
    └── auth.graphql

用户模块 #

graphql
type User implements Node {
  id: ID!
  name: String!
  email: String!
  avatar: String
  status: UserStatus!
  role: Role!
  posts(first: Int, after: String): PostConnection!
  comments(first: Int, after: String): CommentConnection!
  createdAt: DateTime!
  updatedAt: DateTime
}

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  node: User!
  cursor: String!
}

extend type Query {
  user(id: ID!): User
  users(
    first: Int
    after: String
    filter: UserFilter
    orderBy: UserOrderBy
  ): UserConnection!
  me: User
}

extend type Mutation {
  createUser(input: CreateUserInput!): UserMutationPayload!
  updateUser(id: ID!, input: UpdateUserInput!): UserMutationPayload!
  deleteUser(id: ID!): DeleteResult!
}

extend type Subscription {
  onUserCreated: User!
  onUserUpdated(id: ID!): User!
}

公共类型 #

graphql
interface Node {
  id: ID!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

scalar DateTime
scalar JSON
scalar Upload

type DeleteResult {
  success: Boolean!
  message: String
  deletedId: ID
}

type FieldError {
  field: String!
  message: String!
  code: String
}

合并 Schema #

javascript
const { mergeTypeDefs } = require('@graphql-tools/merge');

const userSchema = require('./types/user.graphql');
const postSchema = require('./types/post.graphql');
const commonSchema = require('./types/common.graphql');

const typeDefs = mergeTypeDefs([
  userSchema,
  postSchema,
  commonSchema
]);

Schema 设计最佳实践 #

1. 命名规范 #

graphql
type User {
  id: ID!
  firstName: String!
  lastName: String!
  emailAddress: String!
  profilePicture: String
  createdAt: DateTime!
  updatedAt: DateTime
}

type Query {
  user(id: ID!): User
  users(first: Int, after: String): UserConnection!
  userByEmail(email: String!): User
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): DeleteResult!
}

2. 非空设计 #

graphql
type User {
  id: ID!
  name: String!
  email: String!
  bio: String
  avatar: String
  phone: String
}

3. 分页设计 #

graphql
type Query {
  users(first: Int, after: String): UserConnection!
  posts(first: Int, after: String): PostConnection!
  comments(first: Int, after: String): CommentConnection!
}

4. 过滤和排序 #

graphql
input UserFilter {
  status: UserStatus
  role: Role
  search: String
  createdAt: DateRange
}

input UserOrderBy {
  field: UserOrderField!
  direction: SortOrder!
}

enum UserOrderField {
  NAME
  EMAIL
  CREATED_AT
  UPDATED_AT
}

type Query {
  users(
    filter: UserFilter
    orderBy: UserOrderBy
    first: Int
    after: String
  ): UserConnection!
}

5. 错误处理 #

graphql
type UserMutationPayload {
  user: User
  success: Boolean!
  message: String
  errors: [FieldError!]
}

type FieldError {
  field: String!
  message: String!
  code: ErrorCode!
}

enum ErrorCode {
  VALIDATION_ERROR
  NOT_FOUND
  UNAUTHORIZED
  FORBIDDEN
  CONFLICT
  INTERNAL_ERROR
}

完整示例 #

graphql
scalar DateTime
scalar JSON
scalar Upload

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

enum UserStatus {
  ACTIVE
  INACTIVE
  PENDING
  SUSPENDED
}

enum Role {
  ADMIN
  MODERATOR
  USER
}

enum PostStatus {
  DRAFT
  PUBLISHED
  ARCHIVED
}

enum SortOrder {
  ASC
  DESC
}

interface Node {
  id: ID!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type User implements Node {
  id: ID!
  name: String!
  email: String!
  avatar: String
  status: UserStatus!
  role: Role!
  posts(first: Int, after: String, status: PostStatus): PostConnection!
  comments(first: Int, after: String): CommentConnection!
  createdAt: DateTime!
  updatedAt: DateTime
}

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  node: User!
  cursor: String!
}

type Post implements Node {
  id: ID!
  title: String!
  content: String!
  excerpt: String
  status: PostStatus!
  author: User!
  comments(first: Int, after: String): CommentConnection!
  tags: [String!]!
  viewCount: Int!
  createdAt: DateTime!
  publishedAt: DateTime
  updatedAt: DateTime
}

type PostConnection {
  edges: [PostEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type PostEdge {
  node: Post!
  cursor: String!
}

type Comment implements Node {
  id: ID!
  content: String!
  author: User!
  post: Post!
  parent: Comment
  replies(first: Int, after: String): CommentConnection!
  createdAt: DateTime!
  updatedAt: DateTime
}

type CommentConnection {
  edges: [CommentEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type CommentEdge {
  node: Comment!
  cursor: String!
}

input CreateUserInput {
  name: String!
  email: String!
  password: String!
  role: Role = USER
}

input UpdateUserInput {
  name: String
  email: String
  avatar: String
  status: UserStatus
}

input UserFilter {
  status: UserStatus
  role: Role
  search: String
}

input UserOrderBy {
  field: UserOrderField!
  direction: SortOrder!
}

enum UserOrderField {
  NAME
  EMAIL
  CREATED_AT
}

input CreatePostInput {
  title: String!
  content: String!
  excerpt: String
  status: PostStatus = DRAFT
  tags: [String!]
}

input UpdatePostInput {
  title: String
  content: String
  excerpt: String
  status: PostStatus
  tags: [String!]
}

input PostFilter {
  status: PostStatus
  authorId: ID
  search: String
}

input PostOrderBy {
  field: PostOrderField!
  direction: SortOrder!
}

enum PostOrderField {
  TITLE
  CREATED_AT
  PUBLISHED_AT
  VIEW_COUNT
}

type UserMutationPayload {
  user: User
  success: Boolean!
  message: String
  errors: [FieldError!]
}

type PostMutationPayload {
  post: Post
  success: Boolean!
  message: String
  errors: [FieldError!]
}

type FieldError {
  field: String!
  message: String!
  code: String
}

type DeleteResult {
  success: Boolean!
  message: String
  deletedId: ID
}

type Query {
  me: User
  
  user(id: ID!): User
  users(
    filter: UserFilter
    orderBy: UserOrderBy
    first: Int
    after: String
  ): UserConnection!
  
  post(id: ID!): Post
  posts(
    filter: PostFilter
    orderBy: PostOrderBy
    first: Int
    after: String
  ): PostConnection!
  
  comment(id: ID!): Comment
  
  search(query: String!, type: SearchType, limit: Int = 10): [SearchResult!]!
  
  node(id: ID!): Node
}

enum SearchType {
  USER
  POST
  COMMENT
  ALL
}

union SearchResult = User | Post | Comment

type Mutation {
  createUser(input: CreateUserInput!): UserMutationPayload!
  updateUser(id: ID!, input: UpdateUserInput!): UserMutationPayload!
  deleteUser(id: ID!): DeleteResult!
  
  createPost(input: CreatePostInput!): PostMutationPayload!
  updatePost(id: ID!, input: UpdatePostInput!): PostMutationPayload!
  deletePost(id: ID!): DeleteResult!
  
  createComment(input: CreateCommentInput!): Comment!
  updateComment(id: ID!, input: UpdateCommentInput!): Comment!
  deleteComment(id: ID!): DeleteResult!
}

input CreateCommentInput {
  content: String!
  postId: ID!
  parentId: ID
}

input UpdateCommentInput {
  content: String!
}

type Subscription {
  onUserCreated: User!
  onUserUpdated(id: ID!): User!
  onPostCreated: Post!
  onPostUpdated(id: ID!): Post!
  onCommentAdded(postId: ID!): Comment!
}

directive @auth(requires: Role = USER) on FIELD_DEFINITION
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE

下一步 #

现在你已经掌握了 GraphQL Schema 的设计方法,接下来学习 类型系统,深入了解 GraphQL 的类型机制!

最后更新:2026-03-29