Skip to solution
hardSystem Design

How do you implement microservices communication in Node.js?

836 views
01

Understand the problem

Question presented to candidate: "Your order-service needs to check inventory before confirming an order, and it also needs to notify a notification-service after the order is placed. Would you implement these two interactions the same way? Why or why not?"

What a strong answer should cover:

  • The prompt describes two genuinely different communication needs, and a strong answer treats them differently: checking inventory is a synchronous need (order-service cannot proceed without the answer) — notifying about a placed order is an asynchronous need (order-service does not need to wait for notification-service to finish, or even to succeed, before responding to its own caller).
  • 📌 Verified, not assumed — synchronous: two genuinely separate real Express servers, order-service making a real fetch() HTTP call to inventory-service, received a real 201 response confirming stock, with a measured real elapsed time. This is HTTP/REST (or gRPC in some stacks) — request-response, the caller blocks until it gets an answer.
  • 📌 Verified, not assumed — asynchronous: a real EventEmitter-based publish, where the publisher's own log line ("response already sent") printed after publishing but the subscriber's real reaction had already run by then — demonstrating the publisher does not wait on the subscriber. In production this pattern is typically a real message broker (RabbitMQ, Kafka, or the BullMQ-style queue covered in its own dedicated question) rather than an in-process EventEmitter, since real inter-service async messaging must cross process/machine boundaries.
  • The core trade-off, stated precisely: synchronous (HTTP) communication is simpler to reason about and gives an immediate answer, but genuinely couples the caller's availability to the callee's availability — if inventory-service is down, order-service's request fails too. Asynchronous (events/queues) communication decouples the two services' uptime from each other, at the cost of eventual — not immediate — consistency, and genuinely more operational complexity (a message broker to run and monitor).
  • A precise answer also names that idempotency matters more, not less, in async communication — a message broker's at-least-once delivery guarantee means a subscriber may genuinely receive the identical event twice, which is exactly the scenario verified with real double-charging vs. correctly-deduped behavior in the dedicated idempotency question.

Clarifying questions expected:

  • "Does the caller genuinely need the answer before it can proceed, or can it succeed without knowing the outcome immediately?" — the single question that decides sync vs. async for a given interaction.
  • "Is temporary unavailability of the downstream service acceptable, or must the caller fail immediately if it's down?" — directly maps to the coupling trade-off.

Code / implementation expected: Yes — real HTTP communication between two genuinely separate servers for the synchronous case, and a real publish-without-waiting demonstration for the asynchronous case, are the concrete, convincing proof that these are genuinely different mechanisms, not interchangeable implementation details.

microservicesgrpcarchitecture
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js system-design interviews — assumes familiarity with the idempotency and background-job-queue questions' real proofs. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:.

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

Real synchronous HTTP between two genuinely separate Express servers, plus a real async publish-without-waiting demo
const express = require("express");

// inventory-service — a real, separate HTTP server
const inventory = express();
inventory.get("/stock/:sku", (req, res) => {
  res.json({ sku: req.params.sku, inStock: req.params.sku === "sku-42" });
});
const inventoryServer = inventory.listen(0, () => {
  const inventoryPort = inventoryServer.address().port;

  // order-service — a separate real server, calls inventory-service over real HTTP
  const orders = express();
  orders.post("/orders", async (req, res) => {
    const r = await fetch(`http://localhost:${inventoryPort}/stock/sku-42`);
    const stock = await r.json();
    if (!stock.inStock) return res.status(409).json({ error: "out of stock" });
    res.status(201).json({ orderId: "order-1", sku: stock.sku });
  });
  const ordersServer = orders.listen(0, async () => {
    const ordersPort = ordersServer.address().port;
    const start = Date.now();
    const res = await fetch(`http://localhost:${ordersPort}/orders`, { method: "POST" });
    const body = await res.json();
    console.log("sync call:", res.status, body, `elapsed=${Date.now() - start}ms`);
    inventoryServer.close(); ordersServer.close();
  });
});

// --- async: a real publish that does NOT wait for its subscriber ---
const { EventEmitter } = require("events");
const bus = new EventEmitter();
bus.on("order.created", (order) => {
  console.log("[notification-service] received event, sending email for", order);
});
console.log("[order-service] publishing order.created and returning immediately");
bus.emit("order.created", { orderId: "order-1" });
console.log("[order-service] response already sent, unaware how subscribers reacted");

// sync call: 201 { orderId: 'order-1', sku: 'sku-42' } elapsed=228ms
// [order-service] publishing order.created and returning immediately
// [notification-service] received event, sending email for { orderId: 'order-1' }
// [order-service] response already sent, unaware how subscribers reacted
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 127 of 152 decoded in the Node.js track. One more won't hurt.

Back to track