Home / Articles / 12 Rules of Thumb for Reliable Node.js APIs

This article is published in English.

12 Rules of Thumb for Reliable Node.js APIs

Practical Node.js API reliability rules: timeouts, idempotency, transactions, cache invalidation, logging, and dependency latency—before the 3 a.m. page.

906 words

Some reliability lessons only stick after an outage. The twelve below show up often enough, across enough Node.js services, that stating them clearly is cheaper than rediscovering them under pager pressure. Treat them as defaults when designing handlers, not as optional polish after launch.

1. Retriable requests will be retried.

Not “might.” Clients time out, connections drop mid-response, and people mash submit. Load balancers and mobile networks add their own duplicate deliveries. Any handler that creates or mutates state needs an explicit answer for “what if this identical payload arrives again,” because on a long enough timeline that is not an edge case—it is inevitable. Design the happy path and the duplicate path together.

2. Give every outbound call a timeout.

Database queries, third-party HTTP, payment gateways—if you omit a deadline, a stalled peer can leave the socket open indefinitely. A bare fetch(url) is not “patient for a while”; it is unbounded waiting, and unbounded waits let one slow dependency freeze everything upstream. Prefer library defaults that force a limit, and fail loudly into retries or user-visible errors instead of silent hangs.

3. Browser checks are UX, not a security wall.

Callers can skip your SPA and hit the API with arbitrary JSON using curl, Postman, or a compromised script. Rules that matter—well-formed email, non-negative price, required fields—must be enforced again on the server with the same severity you would apply to hostile traffic. Client validation only helps cooperative users who would not have broken the contract anyway.

4. A cache without an invalidation story is a deferred bug.

Caching creates a second copy of truth, and copies diverge. When you introduce the cache, decide which write, webhook, or TTL event clears it, and document that decision next to the cache key. Relying on “it expires sometime” often means “longer than customers will tolerate wrong balances,” especially when TTLs are measured in hours for “performance.”

5. Coupled writes need a transaction, not optimism.

await update1(); await update2(); looks atomic and is not. Between the awaits a crash, blip, or rejection can leave exactly one update applied, which is how inventory and billing drift apart. Only a real database transaction—or an explicit saga with compensating actions—closes that window with guarantees you can reason about.

6. Instrument for the incident you have not seen yet.

After things break, it is too late to add the log line that would have explained them. Structured logs, correlation IDs that travel across services, and enough context to reconstruct a request—shipped before failure—beat the cleverest postmortem debugging session. Log inputs you can safely keep, outcomes, and dependency latencies while the system is healthy.

7. Process memory is a cache, never the system of record.

A value living only in a Node process vanishes on restart, is invisible to sibling instances, and was never durable. Feature flags held in RAM, counters, or “temporary” Maps all share that fate. If it must survive a deploy, a crash, or sticky-session-free load balancing, put it in a database or another durable store, not a lucky RAM slot.

8. Plan for every network call to fail eventually.

The useful question is not “does this usually work?” but “what happens on the rare miss?” Timeouts, partial responses, and 5xx spikes all count. If the honest answer is “unclear,” close that gap with retries that are safe, circuit breakers, or degraded modes before production discovers the hole.

9. A queue reschedules work; it does not erase it.

Enqueueing feels like completion. A worker still performs the same labor later, with the same failure modes, just shifted in time. If workers lag, the backlog grows even though the API stays responsive—better than blocking requests, but not cheaper work. Size consumers and dead-letter paths as carefully as you size the HTTP tier.

10. Name for six-month readers, not today’s shortcut.

data, temp, and handleStuff stay accurate only until three other concepts also qualify as “data.” Names tied to the business idea survive zero-context readers—including future you on an unfamiliar service. Prefer domain nouns over mechanical verbs that describe the first implementation detail.

11. Creators will be called twice.

Flaky networks, aggressive retry clients, impatient double-taps—any of them. Creation POSTs need graceful duplicate handling, typically via an idempotency key stored with the resulting resource, or double charges and duplicate orders become a matter of timing. Make “already created” return the original result instead of a new side effect.

12. Endpoint latency follows the slowest dependency.

A sleek handler that hits an unindexed query or a remote API with no timeout is still slow. Profiling your own lines while ignoring callees answers the wrong question. Measure p95 and p99 across the full call graph, then fix indexes, batching, and timeouts where the time actually goes.

None of these tips are clever, and that is the point: three-in-the-morning pages rarely are either. They are usually one of these twelve, left unattended until it stopped feeling urgent. Build the boring safeguards in while the service is still small enough to change.