Skip to content
GraphQL API Reference & Cheatsheet — Schema, Queries, Mutations Quick Guide

GraphQL API Reference & Cheatsheet — Schema, Queries, Mutations Quick Guide

DodaTech Updated Jun 6, 2026 6 min read

GraphQL API reference and cheatsheet for schema syntax, resolver signatures, Apollo Server configuration, subscription setup, and common patterns.

What You’ll Learn

  • SDL syntax: types, scalars, enums, interfaces, unions, inputs
  • Query and mutation structure with arguments
  • Resolver function signatures and patterns
  • Apollo Server configuration and plugins
  • Subscription setup with WebSockets
  • Common GraphQL patterns and error codes

Why This Reference Matters

GraphQL has many moving parts — schema syntax, resolver signatures, context management, subscription setup, and security configuration. This cheatsheet puts everything in one place. DodaTech’s engineering team uses it when building Durga Antivirus Pro GraphQL endpoints, ensuring consistent schema design and resolver patterns across all services.

    flowchart LR
    A["GraphQL Reference"] --> B["Schema SDL"]
    A --> C["Resolvers"]
    A --> D["Server Config"]
    A --> E["Subscriptions"]
    A --> F["Common Patterns"]
    style A fill:#dbeafe,stroke:#2563eb
  
Prerequisites: Familiarity with GraphQL. This is a reference for daily use alongside the GraphQL Types Schema and GraphQL Queries Resolvers.

Schema Definition Language (SDL) Syntax

Types

type Device {           # Object type
  id: ID!               # Non-null ID
  name: String!          # Non-null string
  os: String             # Nullable string
  threats: [Threat!]!    # Non-null list of non-null threats
}

scalar DateTime          # Custom scalar
scalar JSON

Enums & Interfaces

enum Severity { LOW MEDIUM HIGH CRITICAL }

interface Node {
  id: ID!
  createdAt: DateTime!
}

type Device implements Node {
  id: ID!
  createdAt: DateTime!
  name: String!
  os: String!
}

union SearchResult = Device | Threat | User

Input Types

input CreateDeviceInput {
  name: String!
  os: String!
  userId: ID!
}

Query & Mutation Structure

# Entry points
type Query {
  device(id: ID!): Device
  devices(limit: Int, offset: Int): [Device!]!
}

type Mutation {
  createDevice(input: CreateDeviceInput!): Device!
  deleteDevice(id: ID!): DeleteResponse!
}

type Subscription {
  threatDetected(deviceId: ID!): Threat!
}

Resolver Signatures

const resolvers = {
  Query: {
    device: (parent, args, context, info) => { },
  },
  Mutation: {
    createDevice: (parent, args, context, info) => { },
  },
  Device: {
    threats: (parent, args, context, info) => {
      // parent = the Device object from the parent resolver
      return context.threatLoader.load(parent.id);
    },
  },
  Subscription: {
    threatDetected: {
      subscribe: (parent, args, context, info) => {
        return context.pubsub.asyncIterator('THREAT_DETECTED');
      },
    },
  },
};

Resolver Arguments

ArgumentDescription
parentReturn value of the parent resolver
argsArguments passed to the field (e.g., {id: "1"})
contextShared state across all resolvers (auth, DB, loaders)
infoQuery execution metadata (field names, selections)

Apollo Server Configuration

const server = new ApolloServer({
  typeDefs,                     // Schema as string or document
  resolvers,                    // Resolver functions
  csrfPrevention: true,         // CSRF protection
  introspection: false,         // Disable in production
  persistedQueries: true,       // Persisted query support
  cache: 'bounded',             // Bounded in-memory cache
  
  context: async ({ req }) => ({
    user: await authenticate(req),
    db: databaseClient,
  }),
  
  plugins: [
    ApolloServerPluginCacheControl({ defaultMaxAge: 300 }),
  ],
  
  validationRules: [
    depthLimit(7),              // Max query depth
  ],
});

Subscription Setup

Server

const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub(); // Use RedisPubSub for production

const server = new ApolloServer({
  typeDefs, resolvers,
  subscriptions: {
    path: '/subscriptions',
    onConnect: (connectionParams, webSocket) => {
      if (connectionParams.authToken) {
        return { user: verifyToken(connectionParams.authToken) };
      }
      throw new Error('Unauthorized');
    },
  },
});

// Publish event
pubsub.publish('THREAT_DETECTED', { threatDetected: threat });

Client

import { WebSocketLink } from '@apollo/client/link/ws';

const wsLink = new WebSocketLink({
  uri: 'ws://localhost:4000/subscriptions',
  options: {
    connectionParams: { authToken: 'token' },
    reconnect: true,
  },
});

Common GraphQL Errors

ErrorCauseFix
Cannot query field "x"Field doesn’t existCheck schema spelling
Field "x" must not have a selectionScalar field with sub-fieldsRemove { } from scalar
Argument "id" of required type "ID!"Missing required argumentProvide the argument
Cannot return null for non-nullable fieldResolver returned nullFix resolver or make field nullable
Query depth limit exceededQuery too nestedReduce nesting or increase limit

Common Patterns

DataLoader

const DataLoader = require('dataloader');

const resolvers = {
  Device: {
    threats: (parent, args, context) => 
      context.threatLoader.load(parent.id),
  },
};

// In context factory
context: {
  threatLoader: new DataLoader(ids => 
    db.threats.findByDeviceIds(ids)
  ),
}

Pagination

type Query {
  devices(first: Int!, after: String): DeviceConnection!
}

type DeviceConnection {
  edges: [DeviceEdge!]!
  pageInfo: PageInfo!
}

Auth Check

const resolvers = {
  Query: {
    myDevices: (parent, args, context) => {
      if (!context.user) throw new AuthenticationError('Not authenticated');
      return db.devices.findByUserId(context.user.id);
    },
  },
};

Common Mistakes

  1. Missing resolvers for nested types — GraphQL uses default property lookup, but it fails for computed or related fields
  2. N+1 queries — always use DataLoader for list child queries
  3. No depth/query cost limits — vulnerable to malicious queries
  4. In-memory PubSub in multi-instance deployments — events don’t cross server boundaries
  5. Exposing internal field names — schema fields should be API-friendly, not database column names
  6. Not using persisted queries — misses caching and security benefits

Practice Questions

  1. What is the resolver chain and how does parent work?
  2. What does [Threat!]! mean in the schema?
  3. Why use DataLoader for nested resolvers?
  4. What does introspection: false do in production?
  5. How do you handle authentication in subscriptions?

Answers:

  1. Each parent resolver’s return becomes the parent argument for its child field resolvers. The root Query resolver has parent: null.
  2. A non-nullable list containing non-nullable Threat objects. The list is always present (possibly empty), and items are never null.
  3. Without DataLoader, fetching 100 devices and their threats makes 101 queries. DataLoader batches into 2 queries.
  4. It disables schema introspection — attackers cannot query __schema to discover your full API.
  5. Pass auth token in WebSocket connectionParams and validate in onConnect callback.

Challenge: Write a complete Apollo Server configuration for Durga Antivirus Pro including: JWT authentication in context, DataLoader for device threats, query depth limit of 7, rate limiting via plugin, Redis-based PubSub for subscriptions, and persisted queries enabled.

FAQ

What is the difference between Apollo Server and Apollo Client?
: Apollo Server runs on the backend — it serves the GraphQL API. Apollo Client runs in the browser or mobile app — it fetches data from the server, manages cache, and provides React/Vue/Angular bindings.
How do I handle file uploads in GraphQL?
: Use the Upload scalar. The resolver receives a { filename, mimetype, createReadStream } object. Pipe the stream to cloud storage (S3, Firebase) and return the URL.
Can I use GraphQL without Apollo?
: Yes — express-graphql (GraphQL.js HTTP server), graphql-yoga (Prisma’s server), and Hasura (auto-generates from Postgres) are alternatives.
What is the best way to cache GraphQL queries?
: Use Apollo Client’s InMemoryCache for client-side caching. For server-side, use responseCachePlugin with Cache-Control directives on individual fields. For CDN caching, use automatic persisted queries (APQ) with a CDN that caches GET requests.

What’s Next

TopicDescription
RESTful APIsCompare with REST for different use cases
Firebase BaaSServerless backend alternative with real-time DB
GraphQL IntroductionReview core GraphQL concepts
SOAP & Web ServicesEnterprise API architecture comparison

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro