FR

Full Stack · 2 min read

Verified Experience Pattern

Building High-Throughput REST APIs

The handful of decisions that determine whether an API holds up under real production load: rate limiting, caching, and pagination, done deliberately.

Node.jsRESTGraphQL
Client
API GatewayRate limiting
Service Layer
Cache + Data Layer

A REST API that works well in staging and struggles in production usually isn't missing some exotic optimization — it's missing a small set of deliberate decisions that only matter at volume.

Rate limiting protects the API from its own popularity

Without rate limiting, a single misbehaving client (a retry loop, a scraper, a bug in a partner integration) can degrade the API for everyone else. Rate limiting at the gateway layer — keyed by API key or client identity, not just IP — keeps one consumer's traffic pattern from becoming everyone's incident.

Return clear rate-limit headers and a 429 with a Retry-After value. Clients that are told when to retry behave much better than clients left to guess.

Pagination is a correctness feature, not just a performance one

An unpaginated "list all" endpoint is fine until the underlying table has enough rows that the response becomes slow, and eventually large enough that it risks timing out or exhausting memory. Cursor-based pagination (rather than offset-based) also avoids the correctness problem where rows shift between pages as data changes concurrently.

Caching at the right layer

Not every cache needs to be Redis. For genuinely public, cacheable responses, HTTP caching headers (ETag, Cache-Control) let a CDN or client cache absorb load before it reaches the API at all. Reserve application-level caching (see Caching Strategies with Redis) for data that's per-user or otherwise not cacheable at the edge.

REST and GraphQL aren't mutually exclusive

REST tends to be the better fit for simple, resource-oriented, cacheable access — especially for external and webhook-style consumers. GraphQL earns its complexity when internal or frontend clients need to compose data from multiple resources in a single round trip and over-fetching/under-fetching with REST would otherwise require a proliferation of bespoke endpoints. Exposing both from the same underlying service layer, rather than picking one dogmatically, is often the pragmatic answer.

Authorization has to be consistent across both

Whichever protocol a request comes through, authentication and authorization should be enforced at a shared layer rather than duplicated per endpoint or per resolver — see Designing Authentication with OAuth 2.0, OIDC and RBAC. Inconsistent enforcement between REST and GraphQL is a common source of access-control bugs.

These decisions — gateway-level rate limiting, cursor pagination, and layered caching — are the backbone of the API layer in the Distributed SaaS Platform system.