GraphQL: Schema и типы

Schema — центральный артефакт GraphQL API, описывающий все доступные типы данных и операции в декларативном языке SDL (Schema Definition Language).

Зачем нужно

Schema служит контрактом между клиентом и сервером: клиент точно знает, какие поля доступны и какого типа. Статическая типизация позволяет валидировать запросы до выполнения, генерировать TypeScript-типы и документацию автоматически. Понимание схемы необходимо для проектирования GraphQL API.

Где используется

  • Определение API на сервере (Apollo Server, graphql-yoga, Nexus)
  • Автоматическая генерация TypeScript-типов (graphql-codegen)
  • Introspection в GraphiQL и Playground для изучения API
  • Code-first и Schema-first подходы в NestJS GraphQL

Скалярные типы

# Встроенные скаляры
String    # строка UTF-8
Int       # 32-битное целое
Float     # число с плавающей точкой
Boolean   # true/false
ID        # уникальный идентификатор (строка или число)

# Кастомные скаляры
scalar Date
scalar JSON
scalar URL

Объектные типы

type User {
  id: ID!           # ! — обязательное поле (non-null)
  name: String!
  email: String!
  age: Int          # null — поле необязательно
  role: UserRole!   # ссылка на другой тип
  posts: [Post!]!   # массив Post, не может быть null и содержать null-элементы
  createdAt: Date!
}

enum UserRole {
  ADMIN
  EDITOR
  VIEWER
}

type Post {
  id: ID!
  title: String!
  body: String!
  author: User!
  tags: [String!]!
}

Query, Mutation, Subscription

type Query {
  user(id: ID!): User
  users(role: UserRole, first: Int, after: String): UserConnection!
  me: User
}

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

type Subscription {
  userCreated: User!
  messageAdded(roomId: ID!): Message!
}

Input-типы

# Input — только для аргументов мутаций, не объектных полей
input CreateUserInput {
  name: String!
  email: String!
  role: UserRole = VIEWER  # дефолтное значение
}

input UpdateUserInput {
  name: String
  email: String
  role: UserRole
}

type DeleteResult {
  success: Boolean!
  message: String
}

Интерфейсы и Union

interface Node {
  id: ID!
}

type User implements Node {
  id: ID!
  name: String!
}

type Post implements Node {
  id: ID!
  title: String!
}

union SearchResult = User | Post | Comment

Частые ошибки

  • Путают type и inputinput нельзя использовать как тип поля, только как аргумент
  • Забывают ! (non-null) — поле возвращает null там, где этого не ожидают
  • [Post] vs [Post!]! — важно понимать все комбинации nullable
  • Используют ID как число — GraphQL всегда сериализует ID как строку

Связанные темы

Ресурсы