Your API works beautifully on a 100 Mbps office connection. Then a user in Kumasi or Maiduguri opens your app on 2G, and the spinner runs until they give up. This is not a user problem — it is an engineering problem, and it is solvable.

Building REST APIs that perform well under constrained connectivity requires deliberate decisions at every layer: the shape of your responses, how you compress data, how aggressively you paginate, and how smartly you cache. This guide walks through each of those levers with concrete Node.js implementation.


1. Compress Every Response — Without Exception

Gzip compression is the lowest-effort, highest-return optimization you can make. A typical JSON response shrinks by 60–80% after compression. On a slow network, that difference is the gap between a usable app and an abandoned one.

In an Express application, enabling this takes about three lines:

const compression = require("compression");

app.use(compression({ level: 6, threshold: 512 }));

The threshold: 512 setting skips compression for responses under 512 bytes — compressing tiny payloads wastes CPU without saving meaningful bandwidth. Level 6 is the sweet spot between compression ratio and processing time.

For higher-throughput services, consider Brotli encoding instead. It achieves 15–25% better compression than Gzip on JSON. Node.js 10.16+ supports it natively via the zlib module, and most modern Android browsers support the br content encoding. Serve Brotli when the Accept-Encoding: br header is present, and fall back to Gzip otherwise.


2. Design Payloads That Send Only What Is Needed

Most APIs return everything and let the client discard what it does not use. That is expensive when every kilobyte costs the user real money on a data bundle.

Sparse fieldsets let clients declare exactly which fields they want:

GET /api/v1/products?fields=id,name,price

On the server, parse the fields query parameter and project only those keys before serialising the response. Libraries like mongoose-lean-virtuals make this trivial for MongoDB-backed services.

Nested resource embedding should be opt-in, not default. A product list endpoint should not embed full category objects unless the client explicitly asks with something like ?include=category. Default to returning only foreign key IDs.

The discipline here is intentional API minimalism: design the smallest useful response first, and add richness only when the client negotiates for it.


3. Paginate Aggressively — and Use Cursor-Based Pagination

Offset-based pagination (?page=2&limit=20) is easy to implement but problematic at scale, especially when users are on slow connections and your dataset is large. Cursor-based pagination is more efficient and more resilient to data changes between requests.

A cursor-paginated response in Node.js looks like this:

  • The client sends ?after=<cursor>&limit=20
  • The server returns the next 20 items and a nextCursor value
  • If nextCursor is null, there are no more pages

Keep default page sizes small — 10 to 20 items for list views. Do not let clients request 500 items in a single call without rate-limiting or authentication requirements. Unbounded queries are a bandwidth attack vector as much as a performance one.


4. Implement Delta Responses for Repeat Requests

A mobile app that polls your API every 60 seconds to refresh a feed is resending and re-receiving data the user already has. Delta responses — sending only what has changed since the client's last request — eliminate that redundancy.

The simplest implementation uses ETags and conditional GET requests:

  1. When you serve a response, hash its content and include an ETag: "abc123" header.
  2. The client stores the ETag and sends it back on the next request as If-None-Match: "abc123".
  3. If the underlying data has not changed, respond with 304 Not Modified and an empty body.

A 304 response typically uses fewer than 200 bytes versus several kilobytes for the full payload. On a network where each round trip costs 800ms, eliminating unnecessary data transfer has a compounding effect on perceived performance.

For more granular deltas, consider adding a ?since=<ISO8601_timestamp> parameter to list endpoints. Return only records created or modified after that timestamp. The client maintains its own local state and merges incoming changes — a pattern that mirrors how Firebase and similar real-time databases work, but without the websocket overhead.


5. Cache at Every Layer — And Make Cache Behaviour Explicit

HTTP caching is frequently configured as an afterthought. For low-bandwidth contexts, it should be a first-class design concern.

Set explicit Cache-Control headers on every endpoint:

  • Cache-Control: public, max-age=300 for data that is safe to cache across users (product catalogues, static reference data)
  • Cache-Control: private, max-age=60 for personalised data
  • Cache-Control: no-store only for genuinely sensitive, real-time data (payment status, authentication tokens)

Beyond HTTP caching, use server-side response caching with Redis for endpoints that are expensive to compute. A city's weather, an exchange rate, or a product catalogue does not need to be re-fetched from your database on every request. Cache the serialised JSON at the Redis layer and serve it directly — bypassing your ORM, your business logic, and your database entirely.

Pair this with a CDN for publicly cacheable endpoints. Services like Cloudflare have edge nodes across Africa now. Serving a cached API response from Lagos or Nairobi instead of a data centre in Frankfurt or Virginia cuts round-trip latency by 100–300ms per request.


6. Monitor Payload Size as a First-Class Metric

You cannot optimise what you do not measure. Add response size logging to your API middleware and track average payload size per endpoint alongside your usual latency and error rate metrics.

Set budget alerts. If your product listing endpoint starts returning payloads over 50 KB, something has changed — a developer added an unintentional eager-load, or a new field bloated the response. Catching that regression early is far easier than debugging it after users in the field start complaining.


Why This Matters for Your Project

If you are building a SaaS product, fintech app, healthtech platform, or any consumer-facing software intended for African markets, your API's bandwidth footprint is a direct determinant of your product's accessibility. Network infrastructure is improving rapidly across the continent, but the reality on the ground today — variable 3G, congested towers, expensive data bundles — means that lean API design is not a nice-to-have. It is the difference between a product that works for your actual users and one that works only in your demo environment. Compression, smart pagination, delta responses, and disciplined caching are not advanced topics reserved for FAANG engineers. They are standard practice for any team that takes its users seriously.