All skills
apollographql avatar

/graphql-schema

@79d2b07 official
by Apollo GraphQLapollographql/skills115 stars
13

Guide for designing and changing GraphQL schemas following industry best practices. Use this skill whenever you create or edit GraphQL SDL, such as a `schema.graphql` file or `typeDefs`, including when: (1) designing a new GraphQL schema or API, (2) adding or changing types, fields, arguments, mutations, or descriptions in an existing schema, (3) reviewing existing schema for improvements, (4) deciding on type structures or nullability, (5) implementing pagination or error patterns, (6) ensuring security in schema design.

Use this Skill: https://skilld.dev/gh/apollographql/skills/graphql-schema

This session only. Nothing lands on disk.

referenceserrors.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Error Design Patterns

This reference covers error handling patterns in GraphQL schema design.

Table of Contents

GraphQL Error Model

GraphQL has a built-in error system with errors in the response:

{
  "data": { "user": null },
  "errors": [
    {
      "message": "User not found",
      "path": ["user"],
      "extensions": {
        "code": "NOT_FOUND"
      }
    }
  ]
}

Built-in Errors: Good For

  • Unexpected errors (bugs, infrastructure issues)
  • Authentication failures (401-level)
  • Authorization failures (403-level)
  • Validation of query itself

Built-in Errors: Bad For

  • Expected business errors (item out of stock)
  • Multiple error types for one operation
  • Errors that need rich data

When to Use Each Pattern

Scenario Pattern
Unexpected server error Built-in errors
Authentication required Built-in errors
User input validation Union result types
Business rule violation Union result types
Partial success possible Union or nullable fields
Multiple error types Union result types

Union-Based Error Pattern

Basic Result Type

type Mutation {
  createUser(input: CreateUserInput!): CreateUserResult!
}

union CreateUserResult = CreateUserSuccess | ValidationError

type CreateUserSuccess {
  user: User!
}

type ValidationError {
  message: String!
  field: String
}

Multiple Error Types

union CreateOrderResult =
  | CreateOrderSuccess
  | ValidationError
  | InsufficientInventory
  | PaymentFailed

type CreateOrderSuccess {
  order: Order!
}

type ValidationError {
  message: String!
  field: String
}

type InsufficientInventory {
  message: String!
  unavailableItems: [OrderItem!]!
}

type PaymentFailed {
  message: String!
  reason: PaymentFailureReason!
  retryable: Boolean!
}

enum PaymentFailureReason {
  CARD_DECLINED
  INSUFFICIENT_FUNDS
  EXPIRED_CARD
  FRAUD_SUSPECTED
}

Client Usage

mutation CreateOrder($input: CreateOrderInput!) {
  createOrder(input: $input) {
    ... on CreateOrderSuccess {
      order {
        id
        total
      }
    }
    ... on ValidationError {
      message
      field
    }
    ... on InsufficientInventory {
      message
      unavailableItems {
        productId
        requestedQuantity
        availableQuantity
      }
    }
    ... on PaymentFailed {
      message
      reason
      retryable
    }
  }
}

Benefits of Union Pattern

  1. Type safety - Clients know all possible outcomes
  2. Rich error data - Each error type has specific fields
  3. Self-documenting - Schema shows what can go wrong
  4. Forced handling - Clients must handle each case

Interface-Based Errors

Error Interface

interface Error {
  message: String!
}

type ValidationError implements Error {
  message: String!
  field: String!
}

type NotFoundError implements Error {
  message: String!
  resourceType: String!
  resourceId: ID!
}

type PermissionError implements Error {
  message: String!
  requiredPermission: String!
}

union UpdateUserResult = User | ValidationError | NotFoundError | PermissionError

Using Interface for Queries

type Query {
  user(id: ID!): UserResult!
}

union UserResult = User | NotFoundError | PermissionError

# Client query:
query GetUser($id: ID!) {
  user(id: $id) {
    ... on User {
      id
      name
    }
    ... on Error {
      message
    }
  }
}

Error Codes

Standardize Error Codes

enum ErrorCode {
  # Validation errors
  VALIDATION_FAILED
  INVALID_INPUT
  REQUIRED_FIELD_MISSING

  # Authentication/Authorization
  UNAUTHENTICATED
  UNAUTHORIZED
  TOKEN_EXPIRED

  # Resource errors
  NOT_FOUND
  ALREADY_EXISTS
  CONFLICT

  # Business logic
  INSUFFICIENT_FUNDS
  LIMIT_EXCEEDED
  OPERATION_NOT_ALLOWED

  # System errors
  INTERNAL_ERROR
  SERVICE_UNAVAILABLE
  RATE_LIMITED
}

type MutationError {
  code: ErrorCode!
  message: String!
  field: String
  details: JSON
}

Error with Code Pattern

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

type CreateUserSuccess {
  user: User!
}

union CreateUserResult = CreateUserSuccess | ValidationError

# Usage enables consistent error handling:
# if (result.__typename === 'ValidationError') {
#   switch (result.code) {
#     case 'ALREADY_EXISTS': ...
#     case 'INVALID_INPUT': ...
#   }
# }

Partial Success

Batch Operations

For operations on multiple items:

input BulkUpdateInput {
  items: [UpdateItemInput!]!
}

type BulkUpdateResult {
  successful: [Item!]!
  failed: [BulkUpdateError!]!
}

type BulkUpdateError {
  index: Int!
  itemId: ID
  error: UpdateError!
}

union UpdateError = ValidationError | NotFoundError | PermissionError

type Mutation {
  bulkUpdateItems(input: BulkUpdateInput!): BulkUpdateResult!
}

Client Usage for Batch

mutation BulkUpdate($input: BulkUpdateInput!) {
  bulkUpdateItems(input: $input) {
    successful {
      id
      name
    }
    failed {
      index
      itemId
      error {
        ... on ValidationError {
          message
          field
        }
        ... on NotFoundError {
          message
          resourceId
        }
      }
    }
  }
}

Warnings Pattern

Return success with warnings:

type ImportResult {
  imported: [Record!]!
  skipped: [SkippedRecord!]!
  warnings: [ImportWarning!]!
}

type SkippedRecord {
  row: Int!
  reason: String!
  data: JSON
}

type ImportWarning {
  row: Int
  message: String!
  severity: WarningSeverity!
}

enum WarningSeverity {
  INFO
  WARNING
  ERROR
}

Nullable Fields for Partial Data

type UserWithExternalData {
  id: ID!
  name: String!
  # These might fail independently
  profileImage: Image           # External service
  socialConnections: [Social]   # External service
  # Errors for each
  profileImageError: String
  socialConnectionsError: String
}

Alternative with explicit result types:

type UserWithExternalData {
  id: ID!
  name: String!
  profileImage: ImageResult!
  socialConnections: SocialConnectionsResult!
}

union ImageResult = Image | FetchError
union SocialConnectionsResult = SocialConnectionList | FetchError

type FetchError {
  message: String!
  service: String!
  retryable: Boolean!
}

Source: SKILL.md on GitHub

No alerts1d5 checks · Risk SAFE
  • Gen Agent Trust Hub1d

    The skill is a comprehensive guide for GraphQL schema design, providing best practices for types, naming, pagination, error handling, and security. It is purely documentation and contains no executable code or malicious instructions.

  • Socket1d

    No alerts

  • Snyk1d

    Risk: LOW · No issues

  • Runlayer7mo

    1/6 files flagged

  • ZeroLeaks5mo

    1 finding · Score: 86/100

Signed by skilld at 79d2b07. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 2 days ago
What it can do
Runs commands
compatibility
Any GraphQL implementation (Apollo Server, graphql-js, Yoga, etc.)
metadata
{
  "author": "apollographql",
  "version": "1.0.2"
}
All 1 allowed tools
Bash(npm:*) Bash(npx:*) Read Write Edit Glob Grep
  • Security
  • graphql
  • schema-design
  • api-design
  • apollo
  • nullability
  • pagination
  • mutations
  • type-design
  • backwards-compatibility

README badge

README badge for apollographql/skills/graphql-schema

Instructional skill covering GraphQL schema design best practices, including type definition patterns, nullability rules, pagination, error handling, and security considerations. Applicable to any GraphQL implementation (Apollo Server, graphql-js, Yoga) and targets decisions around type structures, backwards compatibility, and API usability.

Generated from the current SKILL.md.

Does this skill work with any GraphQL server library?
Yes. The skill is compatible with any GraphQL implementation including Apollo Server, graphql-js, Yoga, and others. It provides design guidance independent of server framework.
What topics does this skill cover?
The skill covers type design, nullability rules, input vs output types, interfaces, unions, pagination patterns, error modeling, security best practices, naming conventions, and mutation design.
Is this for existing schema review or new schema design?
Both. The skill applies when designing a new GraphQL schema, reviewing and improving existing schemas, deciding on type structures, or implementing specific patterns like pagination or error handling.
Does this include code generation or validation tools?
No. This skill is a design guide with reference documentation and best practices. It does not generate or validate schemas automatically.

Generated from the current SKILL.md. These answers refresh after source changes.