GraphQL API Reference & Cheatsheet — Schema, Queries, Mutations Quick Guide
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
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 JSONEnums & 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 | UserInput 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
| Argument | Description |
|---|---|
parent | Return value of the parent resolver |
args | Arguments passed to the field (e.g., {id: "1"}) |
context | Shared state across all resolvers (auth, DB, loaders) |
info | Query 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
| Error | Cause | Fix |
|---|---|---|
Cannot query field "x" | Field doesn’t exist | Check schema spelling |
Field "x" must not have a selection | Scalar field with sub-fields | Remove { } from scalar |
Argument "id" of required type "ID!" | Missing required argument | Provide the argument |
Cannot return null for non-nullable field | Resolver returned null | Fix resolver or make field nullable |
Query depth limit exceeded | Query too nested | Reduce 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
- Missing resolvers for nested types — GraphQL uses default property lookup, but it fails for computed or related fields
- N+1 queries — always use DataLoader for list child queries
- No depth/query cost limits — vulnerable to malicious queries
- In-memory PubSub in multi-instance deployments — events don’t cross server boundaries
- Exposing internal field names — schema fields should be API-friendly, not database column names
- Not using persisted queries — misses caching and security benefits
Practice Questions
- What is the resolver chain and how does
parentwork? - What does
[Threat!]!mean in the schema? - Why use DataLoader for nested resolvers?
- What does
introspection: falsedo in production? - How do you handle authentication in subscriptions?
Answers:
- Each parent resolver’s return becomes the
parentargument for its child field resolvers. The root Query resolver hasparent: null. - A non-nullable list containing non-nullable Threat objects. The list is always present (possibly empty), and items are never null.
- Without DataLoader, fetching 100 devices and their threats makes 101 queries. DataLoader batches into 2 queries.
- It disables schema introspection — attackers cannot query
__schemato discover your full API. - Pass auth token in WebSocket
connectionParamsand validate inonConnectcallback.
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’s Next
| Topic | Description |
|---|---|
| RESTful APIs | Compare with REST for different use cases |
| Firebase BaaS | Serverless backend alternative with real-time DB |
| GraphQL Introduction | Review core GraphQL concepts |
| SOAP & Web Services | Enterprise API architecture comparison |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro