{"id":1078,"date":"2026-10-08T11:52:17","date_gmt":"2026-10-08T03:52:17","guid":{"rendered":"http:\/\/www.holicfactory.com\/blog\/?p=1078"},"modified":"2026-10-08T11:52:17","modified_gmt":"2026-10-08T03:52:17","slug":"how-to-communicate-between-microservices-in-an-express-based-system-4332-1beae6","status":"publish","type":"post","link":"http:\/\/www.holicfactory.com\/blog\/2026\/10\/08\/how-to-communicate-between-microservices-in-an-express-based-system-4332-1beae6\/","title":{"rendered":"How to communicate between microservices in an Express &#8211; based system?"},"content":{"rendered":"<p>Alright, let\u2019s cut to the chase: if you\u2019re building microservices with Express, you already know that one of the biggest headaches isn\u2019t writing the routes\u2014it\u2019s making all those little service pieces talk to each other without everything breaking. As someone who\u2019s been building Express-based tools for teams like yours for years (yeah, I run the vendor side of this stuff), I\u2019ve seen so many teams mess up communication between services and end up with slow, janky, or even down systems. Let\u2019s break this down like we\u2019re chatting over coffee, no boring enterprise jargon. <a href=\"https:\/\/www.fsshijielogistics.com\/express\/\">Express<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.fsshijielogistics.com\/uploads\/44940\/small\/railway-railroad-trucking-to-us288ea.jpg\"><\/p>\n<p>First, let\u2019s get one thing straight: there\u2019s no \u201cone size fits all\u201d here. What works for a tiny 2-service app will crash and burn for a 10-service e-commerce stack. I\u2019ve seen teams use raw HTTP calls like it\u2019s 2010 and wonder why their checkout page takes 3 extra seconds because it\u2019s waiting on the inventory service. I\u2019ve also seen teams overcomplicate it with gRPC for a 2-service internal tool, and now they\u2019re drowning in proto files and error handling. Let\u2019s start with the basics that almost every Express dev should know first, then move to the more advanced stuff that solves real pain points.<\/p>\n<h3>1. The Go-To: Synchronous HTTP (But Don\u2019t Suck At It)<\/h3>\n<p>Let\u2019s be real\u2014most microservice comms start with plain old HTTP. Express makes this easy with built-in <code>fetch<\/code> (or if you\u2019re on an older Node version, <code>axios<\/code> or <code>got<\/code>). Wait, but here\u2019s where teams mess up: they just call another service\u2019s endpoint, no retries, no timeouts, no error handling. If the payment service is slow during a flash sale, your order service is going to hang, and users will see spinning wheels until it times out (which is like 30 seconds\u2014yikes).<\/p>\n<p>Here\u2019s how we do it right at our vendor shop: we wrap all external service calls in a tiny retry wrapper, with exponential backoff. For example, if you call the user service to get a user\u2019s address, you don\u2019t just do <code>fetch('https:\/\/user-service.com\/users\/123')<\/code>. You do something like:<\/p>\n<pre><code class=\"language-js\">const callUserService = async (userId) =&gt; {\n  let retries = 0;\n  const maxRetries = 3;\n  while (retries &lt; maxRetries) {\n    try {\n      const res = await fetch(`https:\/\/user-service.com\/users\/${userId}`, { timeout: 5000 }); \/\/ 5s timeout, not 30!\n      if (!res.ok) throw new Error(`HTTP error: ${res.status}`);\n      return res.json();\n    } catch (err) {\n      retries++;\n      if (retries === maxRetries) throw new Error(`Failed to reach user service after ${maxRetries} retries`);\n      await new Promise(res =&gt; setTimeout(res, 100 * retries)); \/\/ backoff: 100ms, then 200ms, then 400ms\n    }\n  }\n};\n<\/code><\/pre>\n<p>This is non-negotiable. No timeouts = cascading failures. I\u2019ve had a client come to us because their support team was getting 100+ tickets from users who couldn\u2019t check out, and it all traced back to them forgetting to set a timeout on their HTTP calls. We fixed that in an hour, and their checkout speed jumped 40%.<\/p>\n<p>Another quick win here: use service discovery, not hardcoded URLs. If you\u2019re running in Kubernetes or even just a Docker Compose stack, tools like Consul or even just environment variables let you point <code>USER_SERVICE_URL<\/code> to whatever instance is running, instead of changing code every time you deploy a new service. At our shop, we tell all our Express clients to set every service\u2019s base URL as an env var\u2014no more <code>http:\/\/localhost:3001<\/code> scattered through code that\u2019s running in production on a different port.<\/p>\n<h3>2. When Sync Is A Bad Idea: Asynchronous Messaging (Event-Driven Stuff)<\/h3>\n<p>Okay, so sync works for things that need a direct response (like getting a user\u2019s address to show on an order), but what about things that don\u2019t? Like, when a user places an order, you need to update inventory, send an email to the customer, and log the order in your analytics service. If you do that synchronously, the order API will take forever to respond, and if one of those services is down, the order will fail entirely\u2014even if the payment went through. That\u2019s a huge UX fail.<\/p>\n<p>Enter event-driven messaging. This is where services publish events when something happens, and other services subscribe to those events to do their work. For Express, the easiest way to start is with something like RabbitMQ or Kafka\u2014wait, but RabbitMQ is way simpler for small to medium teams, and we recommend it to 90% of our clients before they outgrow it. Here\u2019s how this works in practice: when your order service gets a <code>POST \/orders<\/code> request, it saves the order to its database first, then publishes an <code>order.created<\/code> event with the order ID and user ID. Then:<\/p>\n<ul>\n<li>The inventory service subscribes to <code>order.created<\/code> and deducts the stock<\/li>\n<li>The email service subscribes and sends the confirmation<\/li>\n<li>The analytics service subscribes and logs the data<\/li>\n<\/ul>\n<p>All of this happens asynchronously, so your order API responds in like 200ms instead of waiting for 3 other services. No cascading failures here either\u2014if inventory is down, the order is still saved, and the event will queue up and process when inventory is back up.<\/p>\n<p>Pro tip for Express devs: use <code>amqplib<\/code> for RabbitMQ, it\u2019s lightweight and works great with Express routes. We wrote a starter snippet for our clients that sets up a RabbitMQ producer and consumer in 10 lines of code. No need to overengineer Kafka unless you have 10+ services generating millions of events a day. For most Express-based microservices stacks, RabbitMQ is more than enough, and way easier to debug.<\/p>\n<h3>3. gRPC? Wait, Do You <em>Actually<\/em> Need It?<\/h3>\n<p>I get it, gRPC is cool. It\u2019s fast, uses HTTP\/2, has built-in protobufs. But here\u2019s the thing: 9 times out of 10, Express teams don\u2019t need it. gRPC works way better for services that need super high performance (like a data processing service handling millions of requests) or teams that already use protobufs everywhere. But if you\u2019re building an internal tool with 3 services, and you\u2019re spending your time writing <code>.proto<\/code> files instead of getting features out, that\u2019s wasted time.<\/p>\n<p>That said, there\u2019s a use case: if you have two services that call each other a lot, and speed is critical. For example, a product service that\u2019s called hundreds of times a minute by the search service. We\u2019ve had clients who switched from HTTP to gRPC for that specific call and saw a 2x speedup, which made their search load time go from 1.2s to 0.6s. But again\u2014don\u2019t use it everywhere. Stick with HTTP or messaging for most stuff, only use gRPC when you\u2019ve actually measured that you need the speed.<\/p>\n<h3>4. The Big No-Nos I See Every Week<\/h3>\n<p>Let\u2019s wrap this up with the stuff that will break your system if you do it. First: don\u2019t have tight coupling between services. I\u2019ve seen a team where their order service imports code from their user service, just to get a user\u2019s email. That means if you change anything in the user service, you have to redeploy the order service, and vice versa. That\u2019s not microservices\u2014that\u2019s a monolith split poorly. Use events or API calls, not code imports, to communicate between services.<\/p>\n<p>Second: don\u2019t ignore error handling across services. If your payment service returns a 500, don\u2019t just crash the order service\u2014log the error, maybe retry a few times, and show a friendly error to the user like \u201cWe\u2019re having trouble processing your payment, please try again in a minute.\u201d We have a standard middleware we give all our Express clients that catches errors from external services and translates them into user-friendly messages, no ugly stack traces.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.fsshijielogistics.com\/uploads\/44940\/small\/air-freight-air-trucking-to-mexico8ecb5.jpg\"><\/p>\n<p>Third: don\u2019t forget monitoring. If service A calls service B, and service B is down, you need to know that immediately. We tell all our clients to add tracing (tools like OpenTelemetry work great with Express) so you can see exactly where a request is hanging or failing. If you don\u2019t have visibility into cross-service calls, you\u2019ll spend hours debugging a slow feature instead of fixing it.<\/p>\n<hr \/>\n<p><a href=\"https:\/\/www.fsshijielogistics.com\/air-freight\/air-freight-air-express\/\">Air Freight Air Express<\/a> Look, building microservices with Express doesn\u2019t have to be a nightmare. You don\u2019t need to learn 10 new tools or overcomplicate your stack. Start with safe, retried HTTP calls for things that need a direct response, move to event-driven messaging for the rest, and only reach for gRPC when you\u2019ve proven you need the speed. As someone who\u2019s been supporting Express-based systems for years, I\u2019ve seen all the mistakes, and I can help you avoid them. If you\u2019re building a microservices stack and want to make sure your services talk to each other smoothly without all the headaches, let\u2019s chat procurement\u2014we\u2019ll tailor a solution exactly for your team\u2019s size and needs. Just reach out to get the ball rolling.<\/p>\n<h3>References<\/h3>\n<ul>\n<li>Express.js Official Documentation: Building Microservices<\/li>\n<li>RabbitMQ Official Guides for Node.js<\/li>\n<li>Google Cloud: gRPC vs. REST for Microservices<\/li>\n<li>OpenTelemetry for Node.js Tracing<\/li>\n<\/ul>\n<hr>\n<p><a href=\"https:\/\/www.fsshijielogistics.com\/\">Foshan Shijie International Logistics Co., Ltd.<\/a><br \/>With abundant experience, we are one of the most professional express service suppliers in China. We are committed to offering reliable express service and logistics solutions with low price. If you have any enquiry about quote, please feel free to email us.<br \/>Address: Room 701A, Office Building, Jinbo Commercial Center, No. 88, Guiye Road, Guicheng Street, Nanhai District, Foshan City, Guangdong Province<br \/>E-mail: alicesmith.tradingbusiness@gmail.com<br \/>WebSite: <a href=\"https:\/\/www.fsshijielogistics.com\/\">https:\/\/www.fsshijielogistics.com\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Alright, let\u2019s cut to the chase: if you\u2019re building microservices with Express, you already know that &hellip; <a title=\"How to communicate between microservices in an Express &#8211; based system?\" class=\"hm-read-more\" href=\"http:\/\/www.holicfactory.com\/blog\/2026\/10\/08\/how-to-communicate-between-microservices-in-an-express-based-system-4332-1beae6\/\"><span class=\"screen-reader-text\">How to communicate between microservices in an Express &#8211; based system?<\/span>Read more<\/a><\/p>\n","protected":false},"author":284,"featured_media":1078,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[491],"class_list":["post-1078","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-industry","tag-express-4631-1c8b96"],"_links":{"self":[{"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/posts\/1078","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/users\/284"}],"replies":[{"embeddable":true,"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/comments?post=1078"}],"version-history":[{"count":0,"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/posts\/1078\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/posts\/1078"}],"wp:attachment":[{"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/media?parent=1078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/categories?post=1078"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.holicfactory.com\/blog\/wp-json\/wp\/v2\/tags?post=1078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}