System Integration & API Design: How Services Talk Without Breaking
Building a standalone service is relatively easy. The real headaches start when your service has to talk to 5 other internal microservices, a legacy SQL database, and three third-party payment APIs that randomly time out on Friday afternoon. In this guide, we break down practical system integration — REST vs. gRPC, API gateways, why you need idempotency keys, and how to survive network drops.
👉 Macro Blueprint: Want to see where API gateways and network tiers sit in the architecture? Check out our guide on High-Level Design (HLD).
The First Rule of Distributed Systems: The Network Lies
When you call a function inside your own code (e.g., calculateTotal()), it either succeeds or throws an exception in a few nanoseconds. The memory is right there on your CPU.
When you call another service over the network, you enter a world of chaos:
- Packets get dropped.
- DNS lookups occasionally hang for 2 seconds.
- The downstream service might crash right after charging a credit card, but before sending you the “Success” response.
System integration design is the discipline of making your application resilient when network communication is unreliable.
Synchronous vs. Asynchronous: Choosing Your Communication Style
1. Synchronous (REST & gRPC): When You Need an Answer Right Now
In synchronous communication, your service sends a request and freezes execution waiting for the reply:
- REST (JSON over HTTP): The universal language of the web. Perfect for public APIs and mobile apps. Every developer understands it, and you can inspect requests easily with cURL or your browser.
- gRPC (Protobuf over HTTP/2): Ideal for internal microservices. Instead of bulky human-readable JSON strings, gRPC transmits compact binary bytes over persistent TCP streams. It is up to 7x faster than REST and uses far less CPU.
flowchart LR
Client["Client / Web App"] -->|"Public JSON / REST"| Gateway["API Gateway"]
Gateway -->|"Internal Binary gRPC\n(Fast & Compact)"| S1["Order Service"]
Gateway -->|"Internal Binary gRPC"| S2["User Service"]
2. Asynchronous (Kafka & RabbitMQ): When You Want to Decouple
If your service doesn't need an immediate answer to finish its current job, do not use synchronous HTTP calls.
Use a message queue or event stream instead:
- The Order Service publishes an
OrderPlacedevent to Kafka and returns “Success” to the user in 20 milliseconds. - The Billing Service, Inventory Service, and Email Service all read that event from Kafka at their own pace.
- The superpower: If the email server is undergoing maintenance, orders don't fail! Kafka holds the message safely until the email service comes back online.
The API Gateway: Why You Shouldn't Expose Microservices Directly
If you have 15 microservices, never let mobile phones or web browsers connect to all 15 services directly. If you rename a service or change an internal port, you break millions of mobile app installs that haven't updated yet!
Put an API Gateway (like Kong, Envoy, or AWS API Gateway) at the front door:
flowchart TD
Users(["Mobile Apps & Web Browsers"])
Gateway["API Gateway\n(Rate Limiting, SSL, Auth Token Validation)"]
subgraph InternalVPC["Private Internal Network"]
S1["Billing Service"]
S2["Catalog Service"]
S3["Shipping Service"]
end
Users -->|"Public HTTPS"| Gateway
Gateway --> S1 & S2 & S3
The Gateway acts as a bouncer: it terminates SSL, checks that user JWT tokens are valid, blocks abusive IP addresses with rate limiting, and routes the request to the correct internal service.
The Idempotency Key: How to Never Charge a Customer Twice
Imagine a customer clicking “Pay $100” on an e-commerce checkout.
Your backend contacts Stripe. Stripe charges the card. But right as Stripe sends back the HTTP 200 OK message, a cell tower blip drops the connection. Your backend thinks the request failed and returns an error to the user.
The user naturally clicks “Pay $100” again. Without an idempotency key, you just charged them $200!
Here is how senior engineers fix this with Idempotency Keys:
sequenceDiagram
autonumber
actor User as Customer Phone
participant Server as Payment Backend
participant Redis as Redis Cache
User->>Server: 1. POST /pay {idempotency_key: "tx-987", amount: 100}
activate Server
Server->>Redis: 2. Have we seen "tx-987"?
activate Redis
Redis-->>Server: No, key is new!
deactivate Redis
Server->>Server: 3. Charge credit card successfully
Server->>Redis: 4. Save: "tx-987" -> {status: "PAID", tx_id: "ch_abc"}
Server-->>User: 5. Network blip! User never sees response!
deactivate Server
Note over User: User clicks "Pay $100" again with the SAME key
User->>Server: 6. POST /pay {idempotency_key: "tx-987", amount: 100}
activate Server
Server->>Redis: 7. Have we seen "tx-987"?
activate Redis
Redis-->>Server: Yes! Result is already {status: "PAID", tx_id: "ch_abc"}
deactivate Redis
Server-->>User: 8. Return cached success! (Zero duplicate charge!)
deactivate Server
Postel's Law: The Secret to Long-Lived APIs
Early internet pioneer Jon Postel formulated a rule that every API designer should memorize:
“Be conservative in what you do, be liberal in what you accept from others.”
In API terms:
- When sending data: Stick strictly to the agreed schema. Don't send surprising nulls or weird formats.
- When receiving data: If the other service adds a brand new field to the JSON that you don't recognize yet, do not crash! Simply ignore the extra field and process the rest of the payload. This allows services to deploy new features without breaking older clients.
- To see the macro architectural layout of microservices, read High-Level Design (HLD).
- To configure API authentication, OAuth2, and JWT tokens, read Security Design.
- To dive into Event-Driven architecture and Kafka messaging, read Architectural Design Patterns.
Final Thoughts
System integration design is all about assuming that things will go wrong over the wire. By using API Gateways to shield internal services, implementing idempotency keys to make retries safe, and embracing event streaming when immediate answers aren't needed, you create systems that survive real-world network failures with ease.