Skip to content
GraphQL Complete Guide: Schema-Driven API Design from Scratch

GraphQL Complete Guide: Schema-Driven API Design from Scratch

GraphQL is a query language and runtime for APIs that lets clients request exactly the data they need, eliminating over-fetching and under-fetching common in REST.

What You’ll Learn

  • GraphQL core concepts: schema, queries, mutations, resolvers
  • The type system: objects, scalars, enums, and relationships
  • Writing queries with arguments, aliases, and fragments
  • Mutations for creating, updating, and deleting data
  • Architecture patterns and tooling (GraphiQL, Apollo)

Why GraphQL Matters

REST APIs force fixed response structures. To get a user and their posts, you typically need two requests (GET /users/1, GET /users/1/posts). GraphQL lets you fetch both in one request, specifying exactly the fields you need. DodaTech’s Durga Antivirus Pro dashboard uses GraphQL to fetch device details, scan history, and threat alerts in a single query — each UI component specifies its data needs without backend changes.

    flowchart LR
    A["Client App"] -->|"Single Query"| B["GraphQL API\n(You are here)"]
    B --> C["Type Definitions\n(Schema)"]
    B --> D["Resolvers"]
    D --> E["Database"]
    D --> F["REST APIs"]
    D --> G["External\nServices"]
    style B fill:#dbeafe,stroke:#2563eb
    style C fill:#fef3c7,stroke:#d97706
    style D fill:#dcfce7,stroke:#16a34a
  
Prerequisites: Familiarity with REST concepts. JavaScript knowledge helpful for the examples.

GraphQL vs REST

AspectRESTGraphQL
Data fetchingFixed endpointsClient-specified
Over-fetchingCommon (get entire resource)Never (request only what you need)
Under-fetchingCommon (multiple endpoints needed)Never (one request, nested data)
VersioningURI versioning (/v1/users)No versioning — evolve schema
CachingBuilt-in HTTP cachingManual (Apollo cache, Relay)
File uploadNative (multipart)Requires additional config
Learning curveLowerHigher (schema, resolvers, tooling)
Best forPublic APIs, simple CRUDComplex UIs, dashboards, mobile apps

Core GraphQL Concepts

Schema Definition Language (SDL)

GraphQL APIs are defined by a schema written in SDL:

type Device {
  id: ID!
  name: String!
  os: String!
  lastScan: DateTime
  threats: [Threat!]!
}

type Threat {
  id: ID!
  name: String!
  severity: Severity!
  detectedAt: DateTime!
}

enum Severity {
  LOW
  MEDIUM
  HIGH
  CRITICAL
}

type Query {
  device(id: ID!): Device
  threats(severity: Severity): [Threat!]!
  devices: [Device!]!
}

type Mutation {
  createDevice(name: String!, os: String!): Device!
  deleteDevice(id: ID!): Boolean!
}

Queries (Read Data)

# Request
query GetCriticalThreats {
  threats(severity: CRITICAL) {
    id
    name
    detectedAt
  }
}

# Response
{
  "data": {
    "threats": [
      { "id": "th-001", "name": "Emotet", "detectedAt": "2026-06-06T10:00:00Z" }
    ]
  }
}

Mutations (Write Data)

# Request
mutation CreateNewDevice {
  createDevice(name: "Office-PC", os: "Windows 11") {
    id
    name
    os
  }
}

# Response
{
  "data": {
    "createDevice": {
      "id": "dev-007",
      "name": "Office-PC",
      "os": "Windows 11"
    }
  }
}

Common Mistakes

1. Exposing Database Schema Directly

GraphQL types should reflect your API contract, not your database tables. Map database columns to meaningful GraphQL fields with proper naming.

2. N+1 Query Problem

Without data loaders, each nested field resolver makes a separate database query:

query { devices { threats { name } } }
// N=100 devices → 1 query for devices + 100 queries for threats = 101 queries

Use DataLoader to batch and cache database queries.

3. Ignoring Depth Limits

Malicious clients can craft deeply nested queries that overload your server. Implement query depth limits and complexity analysis.

4. Not Using Fragments

Repeating the same field selections in multiple queries is error-prone. Use fragments to reuse field selections.

5. Forgetting That Every Field Has a Resolver

If a field doesn’t have an explicit resolver, GraphQL defaults to looking for a property with the same name on the parent object. This is convenient but can cause unexpected null errors.

Practice Questions

  1. What is the main advantage of GraphQL over REST?
  2. What problem does the N+1 query problem cause?
  3. What is the difference between a Query and a Mutation?
  4. Why doesn’t GraphQL need versioning?
  5. What is the purpose of DataLoader?

Answers:

  1. Clients specify exactly what data they need in a single request — no over-fetching or under-fetching.
  2. N+1 causes excessive database queries (1 parent query + N child queries), slowing down response times. DataLoader batches child queries into single queries.
  3. Queries fetch data (parallel, cached), Mutations modify data (sequential, side effects).
  4. You can add new fields and types without breaking existing queries. Clients only request what they need, so new fields don’t affect them.
  5. DataLoader batches and caches database queries to solve the N+1 problem, grouping many individual lookups into batch requests.

Challenge: Write a GraphQL schema for Durga Antivirus Pro with types for User, Device, Threat, and Scan. Include queries for fetching a user’s devices and their latest threats, and a mutation for submitting a new threat report.

FAQ

Is GraphQL faster than REST?
: GraphQL reduces round trips (one request instead of many) and eliminates over-fetching (less data transferred). But it adds server-side processing overhead for schema validation and resolver execution. For complex UIs with nested data, GraphQL is often faster. For simple CRUD, REST may be faster.
Can I use GraphQL with an existing REST API?
: Yes — resolvers can call REST APIs as data sources. Apollo Server supports RESTDataSource for wrapping REST endpoints. This lets you add a GraphQL layer on top of existing REST services.
Does GraphQL replace databases?
: No — GraphQL is an API layer, not a database. It sits between clients and your data sources (databases, REST APIs, microservices). You still need a database to store and query data.
What is the GraphQL schema?
: The schema is the contract between client and server. It defines all available types, queries, mutations, and their relationships. It’s written in Schema Definition Language (SDL) and is the single source of truth for the API.

Try It Yourself

Explore a live GraphQL API using the public SpaceX API:

curl -X POST https://api.spacex.land/graphql \
  -H "Content-Type: application/json" \
  -d '{ "query": "query { launches(limit: 3) { mission_name launch_date_utc } }" }'

You get back exactly mission_name and launch_date_utc — no extra fields, no multiple endpoints.

What’s Next

TopicDescription
Types & Schema DesignDefine your schema with objects, enums, and relationships
Queries & ResolversWrite queries and resolver functions
Mutations GuideCreate, update, and delete data
RESTful APIsCompare GraphQL with REST for different use cases