Imagine: your frontend requires data with custom nesting — articles with authors, categories, tags, and dynamic zones. Without proper API configuration, you get N+1 queries, huge JSON responses, and LCP degradation. For instance, a typical request for an article with author and category without optimization generates 4 database queries, increasing TTFB to 2 seconds. After optimization, it's one query and TTFB 200 ms — a 90% reduction. We've optimized such scenarios for 30+ projects over 6 years, using a proven methodology. In this guide, we'll break down how to set up REST and GraphQL in Strapi, avoid common mistakes, and speed up the API up to 10x.
Why Use REST and GraphQL Together?
The REST API in Strapi works out of the box — perfect for simple lists and standard CRUD. GraphQL via the plugin connects in 5 minutes and solves overfetching. GraphQL reduces data transfer volume by up to 80% compared to REST for complex hierarchies — our practice confirms that. We combine them: REST for public endpoints, GraphQL for the admin panel and internal tools. Comparison of capabilities is in the table.
| Criterion | REST API | GraphQL API |
|---|---|---|
| Readiness | Immediately after launch | Requires plugin installation |
| Requests | Multiple requests for relations | One request with nested objects |
| Caching | HTTP cache (ETag, CDN) | More complex (requires persisted queries) |
| Flexibility | Fixed response structure | Client chooses fields |
| Performance | Easier to optimize via DB | Risk of deep nesting and N+1 |
How to Configure REST API?
The Strapi response format is uniform:
{
"data": {
"id": 1,
"attributes": { "title": "Article", "slug": "article", "publishedAt": "..." }
},
"meta": {}
}
For filtering, populate, pagination, and sorting, use query parameters. Example request with all features:
GET /api/articles?filters[status][$eq]=published&filters[price][$gte]=100&populate=author,category&sort[0]=publishedAt:desc&pagination[page]=2&pagination[pageSize]=10&fields[0]=title,slug
Supported operators: $eq, $ne, $lt, $in, $contains, $startsWith, $endsWith, $null, $notNull and their case-insensitive variants with the i suffix. For relations use populate — it can be deep, but limit depth through the middleware strapi::populate-depth (recommended maxDepth: 3) to avoid N+1.
How to Connect and Customize GraphQL?
Install the plugin @strapi/plugin-graphql and add configuration in config/plugins.js (endpoint, limits, disable playground in production). Example GraphQL query to fetch articles with nested data:
query GetArticles($locale: I18NLocaleCode, $page: Int) {
articles(
locale: $locale
pagination: { page: $page, pageSize: 10 }
sort: "publishedAt:desc"
filters: { publishedAt: { notNull: true } }
) {
data {
id
attributes {
title
slug
excerpt
publishedAt
cover { data { attributes { url alternativeText } } }
category { data { attributes { name slug } } }
}
}
meta { pagination { total pageCount } }
}
}
For custom resolvers, use the extensionService. Create a field featuredArticles returning only featured articles:
// src/index.ts
export default {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension')
extensionService.use(({ nexus }) => ({
types: [
nexus.extendType({
type: 'Query',
definition(t) {
t.field('featuredArticles', {
type: 'ArticleEntityResponseCollection',
resolve: async (_root, _args, context) => {
const articles = await strapi.entityService.findMany(
'api::article.article',
{
filters: { featured: true },
populate: ['cover', 'category'],
sort: { publishedAt: 'desc' },
limit: 6,
}
)
return { data: articles }
},
})
},
}),
],
}))
},
}
How to Improve API Performance?
Cache heavy queries with Redis. For example, for featuredArticles:
const redis = new Redis(process.env.REDIS_URL)
const cachedFeatured = await redis.get('featured-articles')
if (cachedFeatured) return JSON.parse(cachedFeatured)
const articles = await strapi.entityService.findMany(...)
await redis.setex('featured-articles', 300, JSON.stringify(articles))
A typical mistake is N+1 queries with aggressive populate. If you use populate=*, Strapi performs separate database queries for each relation. Limiting depth via the strapi::populate-depth middleware and Redis caching eliminate this problem. A well-tuned Strapi GraphQL can be significantly faster than REST for complex queries with many relations.
Additionally, use Varnish or CDN to cache responses of public endpoints. Comparison of caching strategies is in the second table.
| Strategy | When to use | Speed gain |
|---|---|---|
| Redis | Dynamic data, frequent updates | Reduces DB load by 5–10x |
| Varnish | Public, rarely changing endpoints | Response time down to 50 ms |
| CDN | Static assets (images, files) | Proximity to user, reduced TTFB |
Step-by-Step Plan to Optimize Strapi API
- Analyze current requests. Use Strapi's built-in logger or external tools (Kibana) to identify slow endpoints.
- Configure populate-depth. Add the middleware strapi::populate-depth with maxDepth: 3 in configuration.
- Implement Strapi Redis caching. Cache results of heavy queries with TTL 300 seconds — this reduces DB load up to 10x.
- Switch to GraphQL for complex relations. If the frontend requests nested data, use GraphQL — it reduces transferred data up to 80%.
- Monitor and iterate. Regularly check performance via Strapi audit logs and adjust settings.
What's Included in Turnkey API Setup?
- Configuration of REST and/or GraphQL considering load;
- Custom resolvers and middleware;
- Populate optimization and caching (Redis, Varnish);
- Access rights and roles setup;
- Documentation (Postman collection, endpoint description);
- Team training (1–2 sessions);
- 2 weeks of support after delivery.
Timeline
Setting up the Strapi GraphQL plugin, custom resolvers, and query optimization takes from 2 to 5 days. Pricing starts at $1,500 — send us a brief and we will provide a detailed estimate. Proper caching can reduce server costs up to 40%, and the average infrastructure budget saving for our clients is 30%. Our guaranteed performance improvement includes a 90% TTFB reduction. Schedule a consultation to discuss your API details. Contact us.
Source: Strapi official documentation on GraphQL — https://docs.strapi.io/dev-docs/plugins/graphql







