Node.js theorytheory 0/50 · 0%
Capstone · hard

50. Capstone: designing a Node backend for a dApp

Putting the pieces together into one coherent service.

A production-grade Node backend for a dApp typically layers several of this track's topics: an Express-style API with validated routes (topics 25-27), configuration driven by environment variables per chain (topics 7, 24, 44), an indexer polling or subscribing to chain data into a database (topics 29-30, 43), caching for hot reads (topic 33), structured logging and health checks (topics 28, 46), and graceful shutdown plus retry/backoff around every external call (topics 32, 35).

js
async function main() {
  const config = loadConfig();       // fail fast if misconfigured
  const db = await connectDb(config.databaseUrl);
  const app = buildApp({ db, config });
  const server = app.listen(config.port);

  startIndexer({ db, rpcUrl: config.rpcUrl }).catch((err) => log("error", "indexer crashed", { err: err.message }));

  process.on("SIGTERM", async () => {
    server.close();
    await db.close();
    process.exit(0);
  });
}

main().catch((err) => { console.error("fatal startup error:", err); process.exit(1); });

None of these pieces is exotic in isolation — the skill this capstone tests is composing them into a service that stays correct and observable under real network conditions: slow RPC nodes, restarts, and traffic spikes.

Check your understanding

  1. 1. What is the value of failing fast with loadConfig() at the very start of main()?

  2. 2. Why does main() attach a top-level `.catch` at the very end?

  3. 3. What real-world conditions does this capstone architecture specifically defend against?