12 — API Integration Patterns: Sync, Async, Events, Gateways, Queues
Every service-to-service integration I'd built seemed to invent its own shape — until I noticed all the shapes were answers to the same three questions. The model that finally clicked: integration patterns are choices about three things — who waits for whom (sync vs async), who pushes data at whom (events vs polling vs webhooks), and where the seams between services fall (gateways, microservices, BFFs, queues). [1] The patterns aren't interchangeable; each one optimizes for a different coupling and a different failure mode.
The thread connecting these nodes is coupling and timing. A synchronous call tightly couples two services in time — both must be up, simultaneously, for the call to succeed. An asynchronous pattern decouples them — one publishes, the other consumes later, and a queue in between absorbs the difference. Every pattern in this section is a position on that coupling/timing axis, chosen based on which failure modes you can tolerate.
Synchronous vs asynchronous APIs
The first split is the most fundamental [2]. A synchronous API holds the connection open and waits for a response — the caller blocks until the work is done. GET /orders/42 returning the order is synchronous: the client waits, the server computes, the response comes back. This is simple to reason about but breaks down for long-running work — the caller is stuck, the connection is held, and a slow operation cascades into timeouts everywhere.
An asynchronous API doesn't make the caller wait for the result. The common shapes:
- Fire-and-track: POST /jobs returns 202 Accepted with a job ID immediately; the client polls /jobs/{id} (or gets a webhook) for the result.
- Event-driven: the operation is published as an event; whoever cares consumes it whenever.
HTTP/1.1 202 Accepted
Location: /jobs/abc123
// later, the client checks:
GET /jobs/abc123 → { "status": "complete", "result": {...} }The rule I use: if the work is fast, synchronous. The simplicity is worth it. If the work is slow, unreliable, or involves a downstream that might be down, asynchronous — return 202, hand off the work, and let the client retrieve the result later. Forcing a 30-second operation into a synchronous call is how you get cascading timeouts.
Event-driven architecture
Event-Driven Architecture (EDA) is the asynchronous pattern taken to its logical extreme [3]. Services don't call each other directly; they publish events ("order created", "payment received") and react to events they care about. The producer doesn't know — and doesn't need to know — who consumes. This decoupling is EDA's whole appeal: services can be added, removed, or changed without the producer caring, because the producer's only job is to announce what happened.
The tradeoff is a shift in how you think. In a synchronous world you trace a call chain ("A calls B calls C"). In an event-driven world you trace a flow of causality ("A published X, which triggered B, which published Y, which triggered C"). That flow is harder to follow when debugging, because no single call stack captures it. EDA is the right choice when you have many independent reactions to the same facts — "when an order is placed, update inventory, send a confirmation, trigger fulfillment, log to analytics" — and you don't want the order service to know about all of them.
API gateways
An API gateway is a single entry point that sits in front of a set of backend services [4]. Instead of clients calling a dozen services directly, they call the gateway, which routes each request to the right backend, and handles cross-cutting concerns in one place: authentication, rate limiting, request/response transformation, logging, and sometimes request composition (fanning out to multiple services and stitching the responses together).
The gateway is the natural home for the non-business logic every endpoint needs. Without one, every backend service re-implements auth and rate limiting; with one, those concerns live in a single place and the backends focus on their actual job. In microservices architectures a gateway is near-essential — it's what lets clients talk to "the API" rather than to a dozen internal URLs.
Microservices architecture
Microservices break a monolith into small, independently deployable services, each owning a slice of business capability, communicating through APIs (or events) [5]. The benefit is organizational and operational: teams own services end-to-end, deploy them independently, and scale them separately. The cost is distributed-systems complexity — network calls instead of function calls, eventual consistency instead of transactions, and the need for patterns like the gateway, the queue, and the BFF (next) just to keep the surface manageable.
The model I hold: microservices are a trade of implementation simplicity for deployment and team scalability. A monolith is simpler to build and debug; microservices let many teams move fast without stepping on each other. Reaching for microservices before the org actually needs them trades real complexity for hypothetical scale. Most APIs start as a monolith and extract services when (and only when) a real boundary justifies it.
Webhooks vs polling
These are the two ways a client learns about changes on the server [6]. Polling is the client repeatedly asking "anything new?" — simple, but wasteful (most polls return nothing) and latent (the update arrives only on the next poll). Webhooks flip it: the client registers a URL, and the server pushes an HTTP request to it when something happens. Efficient and immediate, but the client must now run a server to receive the webhook, handle retries when that server is down, and verify the webhook's authenticity (anyone can POST to a URL).
The rule: poll for simplicity when changes are infrequent and latency is tolerable; use webhooks when changes matter and the client can receive them. [6] A script that checks a status once an hour is fine polling. A payment provider notifying a merchant of a charge needs a webhook.
The BFF pattern
The Backend for Frontend (BFF) pattern is a specialized gateway: instead of one general-purpose API serving every client, you build a thin, dedicated backend per client type — one for the web app, one for mobile, one for third-parties [7]. Each BFF is tailored to exactly the data shape and interactions its frontend needs, which kills over-fetching and lets each frontend team evolve independently.
The motivation is directly tied to GraphQL's pitch (client-declared shape) but solved differently: instead of one flexible query language, you write purpose-built thin APIs. BFFs are the right call when clients are genuinely different (mobile vs web vs public partners) and a single API shape can't serve them all without over-fetching. The cost is running more services, so the pattern earns its keep when client divergence is real.
Batch processing
Batch processing packs many operations into one request [8]. Instead of a client making 100 individual API calls, it sends one batch request with 100 operations, and the server processes them as a group. The wins are reduced connection overhead, fewer auth checks, and often atomicity (all-or-nothing semantics). The cost is API complexity — error handling for partial failures, ordering, size limits. Batch is the right tool for bulk data import/export and high-volume data-intensive workloads; it's overkill for a single operation.
Messaging queues
A message queue is the infrastructure that makes asynchronous and event-driven patterns work at scale [9]. A producer sends a message to the queue; a consumer pulls messages off at its own pace. The queue is a buffer — it absorbs spikes, decouples producer and consumer timing, and survives consumer downtime (messages wait in the queue until the consumer recovers).
producer → [ message queue (buffer) ] → consumer
decouples timing, absorbs spikes, retries failuresThe benefits are the backbone of reliable distributed systems: scalability (add more consumers to drain the queue faster), fault tolerance (a failed message can be retried or dead-lettered), and decoupling (the producer doesn't wait for or know the consumer). When an operation needs to happen reliably but not synchronously, a queue is almost always the right answer. (The next post covers specific queue technologies — Kafka, RabbitMQ — in more depth.)
How I use this
When connecting services, I ask the timing question first: does the caller need the result now (synchronous) or later (asynchronous)? For multiple independent reactions to the same fact, I reach for events (EDA) rather than wiring the producer to every consumer. For cross-cutting concerns across many services, I put an API gateway in front. When client types diverge enough that one API shape hurts them all, I consider a BFF. And for anything that must happen reliably but not immediately, I put a queue in the middle. The unifying habit is making the coupling/timing choice explicit — defaulting to synchronous calls everywhere is how systems become brittle, because every service becomes critically dependent on every other being up, right now.
References
[1] DZone, "API Integration Patterns," 2024. [Online]. Available: https://dzone.com/refcardz/api-integration-patterns
[2] Nordic APIs, "The Differences Between Synchronous and Asynchronous APIs," 2024. [Online]. Available: https://nordicapis.com/the-differences-between-synchronous-and-asynchronous-apis/
[3] Microsoft Azure, "Event Driven Architecture Style," 2024. [Online]. Available: https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven
[4] Red Hat, "What does an API Gateway do?," 2024. [Online]. Available: https://www.redhat.com/en/topics/api/what-does-an-api-gateway-do
[5] Google Cloud, "What is Microservices Architecture?," 2024. [Online]. Available: https://cloud.google.com/learn/what-is-microservices-architecture
[6] Merge, "Polling vs webhooks: when to use one over the other," 2024. [Online]. Available: https://www.merge.dev/blog/webhooks-vs-polling
[7] bff-patterns.com, "Backend for Frontend," 2024. [Online]. Available: https://bff-patterns.com/
[8] Tyk, "API Design Guidance: Bulk vs Batch Import," 2024. [Online]. Available: https://tyk.io/blog/api-design-guidance-bulk-and-batch-import/
[9] Amazon Web Services, "What is a Message Queue?," 2024. [Online]. Available: https://aws.amazon.com/message-queue/
Knowledge check · Question 1 of 5
An operation takes 30 seconds and involves a downstream that's sometimes down. The right API shape is…
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!