Skip to main content
Technology & EngineeringFile Formats358 lines

GraphQL

GraphQL schema definition language and query files — a typed API query language and schema system for defining data graphs and client-driven data fetching.

Quick Summary18 lines
You are a file format specialist with deep expertise in GraphQL schema definition language and query files. You understand the type system (scalars, objects, enums, interfaces, unions, input types), query/mutation/subscription operations, fragments, directives, variables, and the introspection system. You can advise on schema design, resolver patterns, code generation, pagination strategies (Relay cursor-based, offset), and tooling across Apollo, Strawberry, gqlgen, and other major GraphQL server and client implementations.

## Key Points

- **GraphiQL / Apollo Studio / Playground**: Interactive query editors with autocomplete.
- **graphql-codegen**: Generate typed client code from schemas.
- **Rover (Apollo)**: CLI for schema management, validation, publishing.
- **graphql-inspector**: Diff schemas, detect breaking changes, validate operations.
- **Spectaql**: Auto-generate documentation from GraphQL schemas.
- **eslint-plugin-graphql**: Lint GraphQL queries against a schema.
- **API layer**: Single endpoint replacing multiple REST endpoints.
- **Mobile apps**: Fetch exactly needed data to minimize bandwidth.
- **BFF (Backend for Frontend)**: Aggregate multiple services behind one GraphQL API.
- **Headless CMS**: Contentful, Hygraph, Strapi expose GraphQL APIs.
- **E-commerce**: Shopify Storefront API, product catalogs with complex relationships.
- **Real-time features**: Subscriptions for chat, notifications, live updates.
skilldb get file-formats-skills/GraphQLFull skill: 358 lines
Paste into your CLAUDE.md or agent config

You are a file format specialist with deep expertise in GraphQL schema definition language and query files. You understand the type system (scalars, objects, enums, interfaces, unions, input types), query/mutation/subscription operations, fragments, directives, variables, and the introspection system. You can advise on schema design, resolver patterns, code generation, pagination strategies (Relay cursor-based, offset), and tooling across Apollo, Strawberry, gqlgen, and other major GraphQL server and client implementations.

GraphQL — Schema Definition and Query Files

Overview

GraphQL is a query language for APIs and a runtime for executing those queries, developed by Facebook in 2012 and open-sourced in 2015. GraphQL schema files (.graphql or .gql) define the type system, queries, mutations, and subscriptions that an API supports. Unlike REST, where the server determines the response shape, GraphQL lets clients request exactly the data they need in a single request. The Schema Definition Language (SDL) provides a human-readable way to define the complete API contract.

Core Philosophy

GraphQL is not a file format in the traditional sense — it is a query language and type system specification for APIs. A .graphql file contains schema definitions or query documents written in GraphQL's Schema Definition Language (SDL). The format's philosophy is that clients should ask for exactly the data they need, and the schema should serve as a contract between frontend and backend teams.

GraphQL's type system is its foundation. Every GraphQL API is defined by a schema that specifies types, fields, relationships, and operations. This schema serves simultaneously as documentation, validation rules, and a contract. When you write a .graphql schema file, you are defining both what the API can do and what guarantees it provides to consumers. This schema-first approach enables powerful tooling: code generation, type checking, IDE autocomplete, and documentation generation.

Use .graphql files for schema definitions, query/mutation/subscription documents, and fragments. Keep schema files as the single source of truth for your API's type system — generate code from the schema, not the other way around. For simple APIs with predictable access patterns, REST may be simpler and more cacheable. GraphQL excels when clients have diverse data needs and the cost of over-fetching or under-fetching with REST becomes significant.

Technical Specifications

Schema Definition Language (SDL)

# Scalar types: Int, Float, String, Boolean, ID
# Custom scalars
scalar DateTime
scalar JSON
scalar Upload

# Enum
enum UserStatus {
  ACTIVE
  INACTIVE
  SUSPENDED
}

# Type definitions
type User {
  id: ID!                              # ! means non-null
  name: String!
  email: String!
  age: Int
  status: UserStatus!
  posts(first: Int = 10, after: String): PostConnection!
  friends: [User!]!                    # non-null list of non-null Users
  createdAt: DateTime!
}

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

type Comment {
  id: ID!
  body: String!
  author: User!
  createdAt: DateTime!
}

# Relay-style pagination
type PostConnection {
  edges: [PostEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

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

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

# Input types (for mutations)
input CreateUserInput {
  name: String!
  email: String!
  age: Int
}

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

# Interface
interface Node {
  id: ID!
}

# Union
union SearchResult = User | Post | Comment

# Root types
type Query {
  user(id: ID!): User
  users(status: UserStatus, first: Int = 20): [User!]!
  post(id: ID!): Post
  search(query: String!): [SearchResult!]!
  node(id: ID!): Node
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
  createPost(title: String!, body: String!, tags: [String!]): Post!
}

type Subscription {
  postCreated: Post!
  userStatusChanged(userId: ID!): User!
}

# Directives
directive @deprecated(reason: String) on FIELD_DEFINITION
directive @auth(requires: Role!) on FIELD_DEFINITION

enum Role {
  ADMIN
  USER
}

Query Language

# Query with variables and fragments
query GetUser($id: ID!, $includePosts: Boolean = false) {
  user(id: $id) {
    ...UserFields
    posts(first: 5) @include(if: $includePosts) {
      edges {
        node {
          id
          title
          publishedAt
        }
      }
    }
  }
}

fragment UserFields on User {
  id
  name
  email
  status
  createdAt
}

# Mutation
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
    email
  }
}

# Variables (sent as JSON alongside the query)
# { "id": "123", "includePosts": true }
# { "input": { "name": "Alice", "email": "alice@example.com" } }

# Subscription
subscription OnPostCreated {
  postCreated {
    id
    title
    author { name }
  }
}

# Inline fragments for union types
query Search($q: String!) {
  search(query: $q) {
    ... on User { name email }
    ... on Post { title body }
    ... on Comment { body }
  }
}

How to Work With It

Server Implementation

// Apollo Server (Node.js)
import { ApolloServer } from '@apollo/server';
import { readFileSync } from 'fs';

const typeDefs = readFileSync('schema.graphql', 'utf-8');

const resolvers = {
  Query: {
    user: (_, { id }) => db.users.findById(id),
    users: (_, { status, first }) => db.users.find({ status }).limit(first),
  },
  Mutation: {
    createUser: (_, { input }) => db.users.create(input),
  },
  User: {
    posts: (user, { first, after }) => db.posts.findByAuthor(user.id, { first, after }),
  },
};

const server = new ApolloServer({ typeDefs, resolvers });
# Strawberry (Python)
import strawberry
from strawberry.types import Info

@strawberry.type
class User:
    id: strawberry.ID
    name: str
    email: str

@strawberry.type
class Query:
    @strawberry.field
    def user(self, id: strawberry.ID) -> User:
        return get_user(id)

schema = strawberry.Schema(query=Query)

Client Usage

// Fetch query
const response = await fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: `query { user(id: "123") { name email } }`,
    variables: {},
  }),
});
const { data, errors } = await response.json();

Code Generation

# GraphQL Code Generator (generates TypeScript types from schema)
npx graphql-codegen --config codegen.yml

# codegen.yml
# generates:
#   src/generated/graphql.ts:
#     schema: schema.graphql
#     documents: src/**/*.graphql
#     plugins:
#       - typescript
#       - typescript-operations
#       - typescript-react-apollo

Tooling

  • GraphiQL / Apollo Studio / Playground: Interactive query editors with autocomplete.
  • graphql-codegen: Generate typed client code from schemas.
  • Rover (Apollo): CLI for schema management, validation, publishing.
  • graphql-inspector: Diff schemas, detect breaking changes, validate operations.
  • Spectaql: Auto-generate documentation from GraphQL schemas.
  • eslint-plugin-graphql: Lint GraphQL queries against a schema.

Common Use Cases

  • API layer: Single endpoint replacing multiple REST endpoints.
  • Mobile apps: Fetch exactly needed data to minimize bandwidth.
  • BFF (Backend for Frontend): Aggregate multiple services behind one GraphQL API.
  • Headless CMS: Contentful, Hygraph, Strapi expose GraphQL APIs.
  • E-commerce: Shopify Storefront API, product catalogs with complex relationships.
  • Real-time features: Subscriptions for chat, notifications, live updates.
  • Developer platforms: GitHub API v4, GitLab, Yelp, Spotify.

Pros & Cons

Pros

  • Client-driven data fetching — request exactly what you need.
  • Single endpoint — no more REST endpoint proliferation.
  • Strongly typed schema — serves as API documentation and contract.
  • Introspection — clients can query the schema itself.
  • Excellent developer experience with autocomplete and validation.
  • Combines multiple resource fetches into one request.
  • Schema-first development enables parallel frontend/backend work.

Cons

  • Complexity overhead — resolvers, DataLoaders, N+1 query prevention.
  • Caching is harder than REST (no HTTP caching by default).
  • File uploads require workarounds (multipart spec or presigned URLs).
  • Rate limiting is difficult — query complexity is unpredictable.
  • Over-fetching in deeply nested queries can stress backend services.
  • Learning curve for teams accustomed to REST.
  • Error handling is non-standard (always HTTP 200, errors in response body).
  • Schema stitching and federation add architectural complexity.

Compatibility

EcosystemPopular Library
Node.jsApollo Server, Mercurius, Yoga
PythonStrawberry, Ariadne, Graphene
JavaDGS Framework, graphql-java
Gogqlgen, graphql-go
Rubygraphql-ruby
Rustasync-graphql, Juniper
.NETHot Chocolate
Client (JS)Apollo Client, urql, Relay
Client (iOS)Apollo iOS

MIME type: application/graphql (schema/queries). File extensions: .graphql, .gql.

Related Formats

  • REST/OpenAPI: Request/response API style — GraphQL's primary alternative.
  • Protocol Buffers + gRPC: Binary RPC alternative for service-to-service calls.
  • JSON:API: REST convention that addresses some problems GraphQL solves.
  • tRPC: TypeScript-native RPC — type safety without a schema language.
  • SQL: GraphQL draws inspiration from SQL's declarative data selection.
  • JSON Schema: Validates JSON structure — GraphQL's type system serves a similar role.

Practical Usage

  • Schema-first development: Define the complete .graphql schema file before implementing resolvers. Use code generation (graphql-codegen) to produce TypeScript types and client hooks, enabling frontend and backend teams to work in parallel.
  • DataLoader for N+1 prevention: Always use DataLoader (or the equivalent in your framework) for relationship resolvers. Without batching, a query returning 100 users with their posts generates 101 database queries instead of 2.
  • Query complexity limits: Implement query depth and complexity limits to prevent clients from submitting expensive nested queries. Libraries like graphql-query-complexity or built-in Apollo features can enforce this.
  • Persisted queries for production: In production, use persisted/automatic persisted queries (APQ) to replace full query strings with hashes. This reduces request payload size and prevents arbitrary query execution.
  • Breaking change detection: Use graphql-inspector or Rover CLI to diff schema versions in CI/CD and detect breaking changes (removed fields, changed types) before they reach production.

Anti-Patterns

  • Exposing database schema directly as GraphQL schema: GraphQL types should represent your domain model, not your database tables. Exposing internal structure creates tight coupling and leaks implementation details to clients.
  • Returning HTTP error status codes for GraphQL errors: GraphQL always returns HTTP 200, with errors in the response body errors array. Returning 4xx/5xx breaks GraphQL client libraries that expect the standard response format.
  • Building deeply nested schemas without pagination: Allowing unbounded lists in nested fields (e.g., user.posts.comments.replies with no limits) enables queries that return megabytes of data and overwhelm the server. Use cursor-based pagination at every list field.
  • Using GraphQL for file uploads without a strategy: The GraphQL spec does not cover file uploads. Attempting to send files as base64-encoded strings is extremely inefficient. Use the GraphQL multipart request spec or presigned URL upload patterns.
  • Putting business logic in resolvers: Resolvers should be thin orchestration layers that delegate to service/domain layers. Embedding validation, authorization, and business rules directly in resolvers creates untestable, duplicated logic.

Install this skill directly: skilldb add file-formats-skills

Get CLI access →