The integration works. This lesson is about making it keep working when something upstream does not.
Treat it as a third-party dependency, because it is.
You are reading from a network of independent operators through a gateway. That is more robust than one vendor’s API in the ways that matter, and it is still not infinitely available and not instantaneous.
Everything you would do for any external data dependency applies:
- Timeouts on every call. A gateway request that hangs must not hang your request handler.
- Retries with backoff, bounded. Retrying forever turns a slow upstream into an outage of your own making.
- A circuit breaker. If queries are failing, stop sending them for a while.
- A fallback rendering. Decide what a screen shows when the data is unavailable, before you find out at three in the morning.
Cache aggressively, and be explicit about staleness.
Chain data does not change retroactively, which makes it unusually cacheable. Anything below a confirmed block height is stable and can be cached for a long time.
The right structure is usually two tiers: a short cache for anything touching the chain head, and a long cache for historical data.
The mistake is treating the graph as your cache. Every query costs money and latency. If a screen shows the same figures to every user, compute it once in your backend and serve it from there.
The health check that matters.
The single most valuable addition to any integration is to query indexing status alongside the data, and expose it.
You want to know two things: how far has this deployment indexed, and is it healthy. From those you can distinguish:
- Data is fresh and complete: render normally.
- Data is behind chain head: render with a staleness indicator.
- Deployment has failed: render an explicit error rather than an empty state.
Read-after-write.
A user submits a transaction and immediately expects to see its effect. The indexer serving your next query may not have processed that block yet.
Options, roughly in order of preference:
- Optimistic update. Show the expected result locally and reconcile when the index catches up. Best experience, most work.
- Poll with a deadline. Query until the entity appears or a timeout expires, then say so honestly.
- Tell the user. “This will appear shortly” is an acceptable answer and is better than a screen that appears to have lost their transaction.
What does not work is assuming the write is immediately readable. It is not, by construction, and no amount of retrying at zero delay changes that.
Cost control.
- Measure per feature. Instrument query counts by screen in staging. You will find one component making far more calls than it needs.
- Never query in a render loop. Obvious, and it still happens.
- Paginate. An unbounded collection query grows with the data.
- Alert on volume. A runaway client is a bill, and you want to know from a monitor rather than an invoice.
What single addition best prevents an application showing stale data as though it were current?
It turns three indistinguishable states, fresh, behind, and failed, into three different renderings. Without it, all of them look like an empty result and none of them errors.
A user submits a transaction and immediately queries for its effect. What should the application do?
The indexer serving your next query may not have processed that block. Retrying at zero delay does not change that, and a screen that appears to have lost the user's transaction is worse than a message saying it is on its way.
Which caching approach fits chain data best?
History below a confirmed block height does not change retroactively, so it is unusually cacheable. Only data near the head needs frequent revalidation.
0 of 3 answered