Published on

System Design: GraphQL Crash Course

Authors
  • avatar
    Name
    Loi Tran
    Twitter

Introduction

GraphQL is a modern API query language and runtime developed by Facebook, designed to give clients more control over the data they receive from a server. Unlike traditional REST APIs that expose fixed endpoints tied to specific data structures, GraphQL allows clients to request exactly the fields they need—nothing more, nothing less—through a single flexible endpoint. This helps reduce over-fetching and under-fetching of data, particularly in complex or data-rich applications like mobile apps. GraphQL also supports nested queries, strong typing via its schema system, and real-time updates with subscriptions, making it a powerful alternative to REST for building efficient and expressive APIs.

Github: JS Github: Python

Overview

FeatureGraphQLREST
Endpoint StructureSingle endpoint (/graphql)Multiple endpoints (e.g., /users, /posts)
Data FetchingClient specifies exact fields neededServer defines response; may over/under-fetch
Nested ResourcesSupports nested queries in one requestRequires multiple round-trips
VersioningVersionless (schema evolves gracefully)Often requires versioning (e.g., /v1/)
Transport ProtocolTypically HTTPHTTP
Response FormatJSONJSON, XML, etc.
TypingStrongly typed via schemaTypically untyped or loosely documented
Real-time SupportBuilt-in support with subscriptionsNot natively supported
Tooling & IntrospectionStrong (e.g., GraphiQL, introspection query)Limited, usually via external docs

1. Operation Names & Variables

Variables let you pass dynamic values into queries and mutations, keeping them reusable and secure. Operation names label your query or mutation, making it easier to debug, log, or run multiple operations in the same request.

query GetUser($id: ID!) {
  user(id: $id) {
    name
  }
}

2. Input Types: Instead of passing multiple arguments, use a structured input type — especially helpful for mutation payloads.

input CreateUserInput {
  firstName: String!
  lastName: String!
}

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

3. Enums: GraphQL supports enum types to restrict fields to a limited set of values.

enum Role {
  USER
  ADMIN
}

type User {
  role: Role!
}

4. Fragments

Useful for reusing field selections across queries.

5. Directives (like @include / @skip)

query GetUser($withEmail: Boolean!) {
  user(id: "123") {
    id
    name
    email @include(if: $withEmail)
  }
}

6. Aliases

Rename fields in your response (useful when calling the same field multiple times).

{
  currentUser: user(id: "1") {
    name
  }
  otherUser: user(id: "2") {
    name
  }
}

7. Pagination

Two main styles:

  • Offset-based (limit, offset)
  • Cursor-based (with edges, pageInfo)

Cursor-based is more scalable for large datasets.

8. Nested Resolvers

You might already be doing this, but:

  • Parent → child relationships
  • Example: user.posts where posts is resolved per user

9. Subscriptions (Advanced)

subscription OnMessageSent {
  messageSent {
    content
    sender
  }
}