REST vs GraphQL: choosing the right query surface
REST vs GraphQL: choosing the right query surface
TL;DR
- Both REST CDA and GraphQL CDA are read-only, use the same delivery tokens, and return published content only.
- Choose REST for simple lookups with strong CDN caching; choose GraphQL when you need precise field selection or multi-type aggregation in a single request.
- GraphQL CDA does not support mutations -- all writes go through the REST-based CMA.
Contentstack exposes two Content Delivery API surfaces: a REST API and a GraphQL API. Both return published content, both use the same delivery tokens, and both sit behind CDN-backed infrastructure. The difference is in how you query, what you get back, and which tradeoffs matter for your use case.
Choosing between them is not a philosophical debate. It is a practical decision based on your query complexity, payload requirements, and team familiarity.
Two APIs, one delivery plane
Both the REST CDA and GraphQL CDA share these characteristics:
- Read-only: Neither supports mutations. All content creation, updates, and deletions go through the Content Management API (CMA).
- Published content only: Both return only published entries for the specified environment. For draft content, use the Preview API.
- Delivery token authentication: Both use the same access_token (delivery token) scoped to an environment.
- CDN-backed: Both benefit from Contentstack's CDN layer for caching and global distribution.
The key differences are in query flexibility, response shape, and caching behavior.
REST CDA: predictable queries, CDN-friendly caching
The REST Content Delivery API follows standard REST conventions. You query specific content types, and the API returns all fields for matching entries.
Base URL: https://cdn.contentstack.io (North America region)
Strengths
- Simple and predictable: Each content type has a straightforward endpoint. Fetching all blog posts is a single GET request to /v3/content_types/blog_post/entries.
- Strong CDN caching: REST URLs are inherently cacheable. The same query string produces the same URL, which CDN edge nodes cache effectively.
- Mature SDK support: The JavaScript Delivery SDK, Java SDK, and other language SDKs are built around REST.
- Reference inclusion: The include[] parameter lets you resolve referenced entries in a single request, with configurable depth.
- Rich query operators: Filter with operators like $in, $nin, $gt, $lt, $regex, and more. Sort, skip, and limit results.
Limitations
- Over-fetching: REST returns all fields for every entry. If you only need title and url from a content type with 30 fields, you still receive all 30.
- Fixed response shape: You cannot reshape the response. The structure mirrors the content type schema.
- Multiple round trips for complex data: If you need data from several content types that are not connected by references, you need multiple API calls.
Example: fetching entries with REST
// Fetch published blog posts with author references included
const response = await fetch(
'https://cdn.contentstack.io/v3/content_types/blog_post/entries?' +
'environment=production&include[]=author&locale=en-us',
{
headers: {
api_key: process.env.NEXT_PUBLIC_CONTENTSTACK_API_KEY,
access_token: process.env.NEXT_PUBLIC_CONTENTSTACK_DELIVERY_TOKEN,
},
}
);
const { entries } = await response.json();GraphQL CDA: flexible queries, precise payloads
The GraphQL Content Delivery API provides a schema that mirrors your stack's content types. You write queries that request exactly the fields you need.
Endpoint: https://graphql.contentstack.com/stacks/{api_key}?environment={env}
Strengths
- No over-fetching: Request only the fields your component needs. A card component that needs title, url, and featured_image gets exactly those three fields.
- Single request for complex data: Fetch entries from multiple content types in one query. A homepage might need hero content, featured articles, and navigation items - all in one request.
- Schema introspection: Use GraphQL tooling to explore your content model. The schema is auto-generated from your content types.
- Type safety: GraphQL's type system provides compile-time guarantees when combined with code generation tools.
Limitations
- No mutations or subscriptions: GraphQL CDA is read-only. You cannot create, update, or delete content through GraphQL. There are no real-time subscriptions.
- Query complexity: Deeply nested queries that traverse many reference levels can hit complexity limits. Contentstack applies query complexity scoring to prevent expensive queries.
- CDN caching nuances: POST-based GraphQL requests are harder to cache at the CDN edge compared to REST GET requests. Contentstack mitigates this, but cache hit rates may differ for highly dynamic query patterns.
- SDK differences: Not all Contentstack SDKs have first-class GraphQL support. The JavaScript SDK primarily targets REST.
Example: fetching entries with GraphQL
query HomepageData {
all_blog_post(locale: "en-us", limit: 5) {
items {
title
url
featured_imageConnection {
edges {
node {
url
title
}
}
}
authorConnection {
edges {
node {
... on Author {
title
bio
}
}
}
}
}
}
}Note the Connection pattern for references and assets. GraphQL CDA uses Relay-style connections for relationships between content types.
Decision framework: when to use which
| Factor | Choose REST | Choose GraphQL |
| Query complexity | Simple lookups, list pages, single content type | Multi-type aggregation, complex nested data |
| Payload efficiency | Not a concern (or you need all fields) | Critical - mobile apps, bandwidth-sensitive |
| Caching requirements | Maximum CDN cache hit rates | Acceptable CDN behavior, query flexibility matters more |
| Team familiarity | Team knows REST, wants quick integration | Team uses GraphQL elsewhere, wants consistency |
| SDK usage | Using Contentstack JavaScript/Java SDK | Building custom fetch layer or using GraphQL client |
| Reference depth | 1-2 levels of includes | Deeply nested content graphs |
Common patterns in production
Pattern 1: REST for most pages, GraphQL for aggregation
Many teams use the REST SDK for standard page rendering (article detail, product page) and GraphQL for pages that pull from many content types (homepage, search results, dashboards).
Pattern 2: GraphQL for frontend, REST for backend jobs
Frontend applications benefit from GraphQL's precise payloads. Backend services like webhook handlers, migration scripts, and content sync jobs use REST for its simplicity and predictable behavior.
Pattern 3: All-REST with SDK
Teams that prioritize SDK convenience and don't have complex aggregation needs stay entirely on REST. The JavaScript SDK handles query building, reference inclusion, and pagination.
Mixing both in one project
There is no rule against using both APIs in the same project. They share the same authentication tokens and return the same published content. The choice is per-query, not per-project.
If you do mix them, establish a convention:
- Document which queries use which API surface
- Centralize API client configuration so token management is consistent
- Monitor cache behavior for both to understand performance characteristics
Common mistakes
1. Using GraphQL to avoid learning the REST query syntax
GraphQL is not "better REST." If your queries are simple and the SDK handles them well, GraphQL adds complexity without benefit. Choose based on actual needs, not preference.
2. Ignoring GraphQL query complexity limits
Deeply nested queries that traverse many reference levels can be rejected or throttled. Design your queries with awareness of complexity scoring. Flatten where possible.
Common pitfall:
Assuming GraphQL can handle content mutations leads teams to architect write operations against an API that only supports reads -- all writes, workflow changes, and administrative actions require the REST-based CMA.
3. Assuming GraphQL replaces CMA
GraphQL CDA is read-only. Teams sometimes expect to use GraphQL for content operations. All writes, workflow changes, and administrative actions require the REST-based Content Management API.