REST Communication Between Microservices — Complete Guide
In this tutorial, you will learn about REST Communication Between Microservices. We cover key concepts, practical examples, and best practices to help you master this topic.
REST communication uses HTTP methods and status codes for synchronous service-to-service interactions, relying on stateless requests and standardized resource representations for interoperability.
What You'll Learn
By the end of this lesson you will design RESTful APIs for microservices communication, handle errors consistently, implement API versioning, manage request timeouts, and structure service-to-service HTTP calls.
Why It Matters
REST is the most widely adopted protocol for microservices communication. Its simplicity and ubiquity make it the default choice for synchronous interactions. Poorly designed REST interfaces create brittle integrations, inconsistent error handling, and versioning nightmares.
Real-World Use
DodaZIP's file service exposes a REST API that the web frontend and mobile apps use to upload files, check processing status, and retrieve results. Each endpoint follows consistent patterns for pagination, error responses, and Rate Limiting.
flowchart LR
A[Service A] -->|GET /api/v1/users/123| B[User Service]
B -->|200 OK + JSON| A
A -->|POST /api/v1/files| C[File Service]
C -->|201 Created + Location| A
style B fill:#2d3748,color:#fff
style C fill:#2d3748,color:#fff
REST Principles for Microservices
Core REST constraints applied to service communication.
# rest_principles.py
# REST principles for microservices
def rest_principles():
principles = {
"Stateless": "Each request contains all information needed. No server-side session state.",
"Resource-based": "URLs represent resources (nouns), not actions (verbs).",
"HTTP Methods": "GET (read), POST (create), PUT (replace), PATCH (update), DELETE (remove).",
"Status Codes": "Standard HTTP codes indicate success, client error, or server error.",
"Uniform Interface": "Consistent patterns for collection URLs, pagination, filtering.",
}
print("REST Principles for Microservices")
print("=" * 40)
for principle, desc in principles.items():
print(f"\n{principle}:")
print(f" {desc}")
rest_principles()
Service-to-Service Request Pattern
How one service calls another via REST.
# service_call.py
# Service-to-service REST call
def service_call_pattern():
print("Service-to-Service REST Call Pattern")
print("=" * 40)
print()
print("Request:")
print(" GET /api/v1/orders?status=pending&page=1")
print(" Headers:")
print(" Authorization: Bearer <service-token>")
print(" X-Request-ID: <trace-id>")
print(" Accept: application/json")
print()
print("Response (200 OK):")
print(" {")
print(' "data": [{ "id": 1, "total": 29.99 }],')
print(' "pagination": {')
print(' "page": 1,')
print(' "per_page": 20,')
print(' "total": 142')
print(" }")
print(" }")
print()
print("Error Response (4xx/5xx):")
print(" {")
print(' "error": {')
print(' "code": "ORDER_NOT_FOUND",')
print(' "message": "Order with id 999 not found",')
print(' "request_id": "req-abc-123"')
print(" }")
print(" }")
service_call_pattern()
Error Handling Strategy
Consistent error responses across services.
# error_handling.py
# REST error handling patterns
def error_handling():
error_codes = {
"400": {
"name": "Bad Request",
"meaning": "Invalid input format or missing required fields",
"action": "Fix the request data before retrying"
},
"401": {
"name": "Unauthorized",
"meaning": "Missing or invalid authentication token",
"action": "Obtain a valid token and include it in headers"
},
"403": {
"name": "Forbidden",
"meaning": "Authenticated but not authorized for this resource",
"action": "Request elevated permissions or use different credentials"
},
"404": {
"name": "Not Found",
"meaning": "The requested resource does not exist",
"action": "Verify the resource identifier"
},
"409": {
"name": "Conflict",
"meaning": "Request conflicts with current state (e.g., duplicate)",
"action": "Check current state and retry with corrected data"
},
"429": {
"name": "Too Many Requests",
"meaning": "Rate limit exceeded",
"action": "Retry after the time specified in Retry-After header"
},
"500": {
"name": "Internal Server Error",
"meaning": "Unexpected server failure",
"action": "Retry with exponential backoff; report if persistent"
},
"503": {
"name": "Service Unavailable",
"meaning": "Service temporarily unavailable (e.g., maintenance)",
"action": "Retry after a delay; check service health endpoint"
},
}
print("HTTP Status Codes for Microservices REST")
print("=" * 50)
for code, info in error_codes.items():
print(f"\n{code} {info['name']}")
print(f" Meaning: {info['meaning']}")
print(f" Action: {info['action']}")
error_handling()
API Versioning
Strategies for evolving REST APIs without breaking consumers.
# versioning.py
# API versioning strategies
def versioning_strategies():
print("API Versioning Strategies")
print("=" * 35)
print()
strategies = [
{
"method": "URL Path",
"example": "/api/v1/users, /api/v2/users",
"pros": "Explicit, easy to route, cache-friendly",
"cons": "URL pollution, requires redirects or separate deployments"
},
{
"method": "Header",
"example": "Accept: application/vnd.api+json;version=2",
"pros": "Clean URLs, standard HTTP content negotiation",
"cons": "Less visible, harder to test manually"
},
{
"method": "Query Parameter",
"example": "/api/users?version=2",
"pros": "Simple to implement, easy to test",
"cons": "Uncached easily, pollutes query strings"
},
]
for s in strategies:
print(f"Strategy: {s['method']}")
print(f" Example: {s['example']}")
print(f" Pros: {s['pros']}")
print(f" Cons: {s['cons']}")
print()
versioning_strategies()
Common Mistakes
Using verbs in URLs: URLs should represent resources (nouns), not actions. Bad:
/createOrder. Good:POST /orders.Inconsistent error format: Each service returning different error shapes forces consumers to handle every variant. Use a standardized error envelope across all services.
Ignoring idempotency: POST requests that create resources can be retried. Without idempotency keys, network retries create duplicate resources.
No pagination for collections: Returning all records in a single response works in development but crashes production under load. Always paginate collection endpoints.
Exposing internal IDs externally: Using database primary keys in URLs exposes your data model and creates security risks. Use UUIDs or opaque identifiers instead.
Practice Questions
What is the correct HTTP method for creating a resource? POST. For full replacement, use PUT. For partial update, use PATCH.
What status code indicates a service is rate-limiting requests? 429 Too Many Requests. The client should respect the Retry-After header.
Why should microservices use pagination in REST responses? To prevent memory exhaustion, reduce latency, and avoid transferring unnecessary data.
What is an idempotency key and why is it important? A unique identifier sent with POST requests so the server can detect and reject duplicate submissions.
Challenge: Design a REST API for a notification service that supports email, SMS, and push notifications. Define the resources, methods, status codes, error format, and pagination strategy.
FAQ
Mini Project
Design a RESTful API for a microservice that manages user preferences across multiple services (theme, notifications, privacy settings). Define the resource model, endpoints, status codes, error format, and versioning strategy. Include pagination and idempotency.
def preferences_api():
print("User Preferences REST API Design")
print("=" * 40)
print()
print("Resource: /api/v1/users/{userId}/preferences")
print()
print("Endpoints:")
print(" GET /preferences - List all preferences")
print(" GET /preferences/{key} - Get single preference")
print(" PUT /preferences/{key} - Set preference value")
print(" PATCH /preferences - Bulk update preferences")
print(" DELETE /preferences/{key} - Reset to default")
print()
print("Response: {")
print(' "preferences": {')
print(' "theme": "dark",')
print(' "notifications": true,')
print(' "language": "en"')
print(" },")
print(' "version": 2')
print("}")
preferences_api()
What's Next
Next: gRPC Introduction for high-performance RPC communication.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro