Do your Node.js back-ends really need service discovery?
Consul, etcd, DNS-SD — service discovery is a lot of machinery to stand up. Sometimes you genuinely need it; often you don't. Here's how to tell, and how a queue makes the question disappear.
Service discovery — Consul, etcd, Eureka, DNS-SD, or your platform's built-in registry — solves a real problem: when service A wants to call service B, how does A find a healthy instance of B? But it's also a substantial piece of infrastructure to run, secure, and reason about. Before you add it, it's worth asking whether your architecture actually needs it.
What service discovery is really for
Discovery exists because, in an HTTP world, you call an address. To call B, you need B's host and port, and since instances come and go, you need something that keeps a live map of "where is a healthy B right now." That something is your discovery layer, usually paired with health checks and often a load balancer or mesh.
That's the right tool when:
- You have a polyglot fleet where many kinds of clients need to locate services.
- You're already invested in a service mesh and want its traffic policies.
- You need discovery for things beyond RPC — config, leader election, distributed coordination.
If that's you, keep your discovery layer; it earns its place.
When it's overkill
For a lot of Node.js back-ends, discovery is machinery bolted on to answer a question you could avoid asking. If services communicate through a message queue, addressing changes shape entirely: a service reads from its own named queue, and a caller sends to that name. There's no host to resolve, because the queue name is the address.
- Instances of a service all read the same named queue, so "which instance" is decided by the queue, not by a registry.
- Starting or stopping instances doesn't change anything anyone has to discover — they just attach to (or detach from) the same queue.
- There's no live host map to maintain, no health-check plumbing feeding a registry.
With @imqueue you call a service by using its generated client; the name wiring is handled for you:
const users = new UserServiceClient();
await users.start();
const u = await users.get('42'); // no host, no port, no registry lookup
The caller never knows or cares how many instances of UserService exist or where they run.
A fair accounting
This doesn't make coordination problems vanish — it moves them:
- You now depend on the message queue being available, the way you'd depend on a registry or mesh. For most teams that's infrastructure they already operate.
- If you need discovery for non-RPC reasons (config distribution, coordination), a queue doesn't replace those — use the right tool for them.
- Cross-language fleets may still want a language-neutral discovery story.
But if the honest reason you were about to install Consul is "so my Node.js services can find each other to make calls," a queue-based model likely makes that need disappear — one less distributed system to run. See Getting Started for how calls work without any addressing on your side.
Building on @imqueue? The open-source packages live on GitHub and the docs at imqueue.org. Shipping inside a closed-source product? See commercial licensing & support.