Skip to content

Performance optimization

Performance Optimization

When to Use

Essential for production deployments, high-traffic APIs, and mobile applications.

Decision

Optimization Impact Complexity When to Apply
Enable caching High Low All deployments
Use sparse fieldsets High Low Mobile, large responses
Limit includes Medium Low Always (selective)
Implement pagination High Low All collections
CDN/reverse proxy High Medium Production, high traffic
Disable unused resources Low Low All deployments
Index filter fields Medium Low Filtered collections

Pattern

Enable Drupal caching:

// settings.php
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.database';
$settings['cache']['bins']['page'] = 'cache.backend.database';

// JSON:API responses are cached automatically

Use sparse fieldsets:

# Bad: Fetch all fields
GET /jsonapi/node/article

# Good: Fetch only needed fields
GET /jsonapi/node/article?fields[node--article]=title,created

Result: 70% smaller response, 40% faster processing.

Limit includes:

# Bad: Include all relationships
GET /jsonapi/node/article?include=uid,field_image,field_tags

# Good: Include only what's rendered
GET /jsonapi/node/article?include=uid&fields[user--user]=name

Implement pagination:

# Bad: Fetch unbounded collection
GET /jsonapi/node/article

# Good: Paginate with reasonable limit
GET /jsonapi/node/article?page[limit]=25

CDN caching (Varnish example):

# In Varnish VCL
sub vcl_recv {
  # Cache JSON:API GET requests
  if (req.url ~ "^/jsonapi/" && req.method == "GET") {
    return (hash);
  }
}

sub vcl_backend_response {
  # Respect Cache-Control headers from Drupal
  if (bereq.url ~ "^/jsonapi/") {
    set beresp.ttl = 1h;
  }
}

Index filter fields:

// In hook_update_N() or during field creation
$field_storage = FieldStorageConfig::loadByName('node', 'field_category');
$schema = $field_storage->getSchema();
$schema['indexes'] = ['value' => ['value']];
$field_storage->set('schema', $schema);
$field_storage->save();

Disable unused resources:

# Via JSON:API Extras
# Reduces resource type discovery overhead
id: taxonomy_term--tags
disabled: true

Metrics

Before optimization:

Response size: 450 KB
Response time: 1,200 ms
Database queries: 47

After optimization:

Sparse fieldsets: 180 KB (60% reduction)
Pagination: 600 ms (50% faster)
Database queries: 12 (75% reduction)
CDN hit: 15 ms (99% faster on cache hit)

Common Mistakes

Not leveraging Drupal's cache system: Rebuilding responses for identical requests. WHY: JSON:API respects Drupal cache tags. Enable dynamic_page_cache.

Fetching entire collections repeatedly: Loading thousands of nodes on every request. WHY: Pagination exists for a reason. Always use page[limit].

Including relationships not displayed: Adding ?include=field_tags when tags aren't shown. WHY: Each include adds database joins and normalization overhead.

Not using CDN for anonymous traffic: Application server handles all requests. WHY: JSON:API responses are highly cacheable. Use Varnish, Cloudflare, or Fastly.

Sorting by non-indexed fields: Sorting by body text or long_text fields. WHY: Database can't efficiently sort without index. Sort by created, changed, or indexed fields.

Not monitoring performance: Assuming optimization worked without measuring. WHY: What gets measured gets improved. Use New Relic, Blackfire, or similar APM tools.

See Also