GraphQL
ActiveGraphQL is a query language for APIs and a runtime for executing those queries. Clients specify exactly what data they need in a single request. A strongly-typed schema defines every field and relationship. Originally created at Facebook in 2012, open-sourced in 2015, and now governed by the GraphQL Foundation under the Linux Foundation.
In one line
GraphQL (spec: October 2021) is a query language and server-side runtime for APIs defined by a strongly-typed schema. Clients send a single POST to one endpoint specifying exactly the fields they need. Three operation types: query (read), mutation (write), and subscription (real-time). Every field in the response is declared in the schema. The type system enables introspection – clients can query the schema itself at runtime. Widely deployed at Facebook, GitHub, Shopify, Twitter, and Airbnb.
Quick Reference
| Field | Size | Description |
|---|---|---|
| Transport | HTTP (typically) | GraphQL is transport-agnostic. In practice: POST /graphql with Content-Type: application/json. GET also supported for queries without variables. |
| Endpoint | Single URL | All operations (query, mutation, subscription) go to one endpoint – typically /graphql. Contrast with REST where each resource has its own URL. |
| Request body | JSON | { "query": "...", "variables": {...}, "operationName": "..." }. The query field is required. variables and operationName are optional. |
| Response Content-Type | application/graphql-response+json | Defined by GraphQL over HTTP spec (draft). Older servers return application/json. Response always contains a data key and/or an errors key. |
| Operation types | 3 | query (read), mutation (write/side-effects), subscription (real-time event stream over WebSocket or SSE). |
| Type system | Scalar + Object + ... | Built-in scalars: Int, Float, String, Boolean, ID. Custom scalars allowed. Object, Input, Enum, Interface, Union, List, NonNull types. |
| Introspection | __schema / __type | Every GraphQL server exposes __schema and __type meta-fields. Clients can query the full type system at runtime. Disable in production to reduce attack surface. |
| N+1 problem | DataLoader pattern | Naive resolvers make one DB query per list item. DataLoader batches and deduplicates queries within a single request tick. |
Key Characteristics
Declare exactly what you need
Clients specify every field they want. No over-fetching (extra fields), no under-fetching (missing fields needing a second request). One round trip.
Strongly typed schema
Every field, argument, and return type is declared in the schema definition language (SDL). Type errors caught at validation before execution.
Single endpoint
All operations – across every resource type – go to one URL. Routing is handled by operation name and type, not by URL structure.
N+1 and depth attacks
Deeply nested queries can trigger exponential DB calls. Always implement query depth limits, complexity limits, and DataLoader for list resolvers.
Message Format
# GraphQL request over HTTP POST
POST /graphql HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer <token>
{
"query": "query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts(first: 5) {
title
createdAt
}
}
}",
"variables": { "id": "usr_123" },
"operationName": "GetUser"
}# GraphQL success response
HTTP/1.1 200 OK
Content-Type: application/graphql-response+json
{
"data": {
"user": {
"id": "usr_123",
"name": "Alice",
"email": "[email protected]",
"posts": [
{ "title": "Hello World", "createdAt": "2026-01-01T00:00:00Z" }
]
}
}
}
# GraphQL error response (HTTP 200 – always 200)
{
"data": null,
"errors": [
{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"],
"extensions": { "code": "NOT_FOUND" }
}
]
}