A Comprehensive Guide to Standardized Contracts and Microservice Design
Table of Contents
- Introduction
- Fundamentals of API Design
- REST Architecture Principles
- API Contract Specification
- HTTP Methods and Status Codes
- URL Design and Resource Naming
- Request and Response Formats
- Error Handling Patterns
- Authentication and Authorization
- Versioning Strategies
- Microservice Communication Patterns
- API Gateway Patterns
- Testing Strategies
- Performance and Scalability
- Monitoring and Observability
- Security Best Practices
- Documentation Standards
- Implementation Examples
Introduction
What is API Endpoint Architecture?
API (Application Programming Interface) endpoint architecture refers to the systematic design and organization of communication points between different software systems. In microservice architectures, well-designed API endpoints serve as the foundation for scalable, maintainable, and interoperable systems.
Why Standardization Matters
graph TB
A[Standardized APIs] --> B[Improved Developer Experience]
A --> C[Reduced Integration Time]
A --> D[Better Maintainability]
A --> E[Enhanced Scalability]
B --> F[Consistent Patterns]
C --> G[Predictable Behavior]
D --> H[Clear Documentation]
E --> I[Loose Coupling]Key Benefits of Standardized API Contracts
- Consistency: Uniform patterns across all services
- Interoperability: Language-agnostic communication
- Maintainability: Easier to update and modify
- Testability: Standardized testing approaches
- Documentation: Clear and consistent API documentation
Fundamentals of API Design
Core Design Principles
1. Contract-First Design
sequenceDiagram
participant D as Designer
participant S as Specification
participant Dev as Developer
participant C as Consumer
D->>S: Define API Contract
S->>Dev: Implement Against Contract
S->>C: Understand Expected Behavior
Dev->>C: Deliver Consistent Interface2. Language-Agnostic Contracts
API contracts should be independent of implementation language:
Good Contract Example:
# OpenAPI 3.0 Specification
openapi: 3.0.0
info:
title: User Management API
version: 1.0.0
paths:
/users:
get:
summary: Retrieve users
responses:
'200':
description: List of users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'YAML3. Resource-Oriented Design
graph LR
A[Client] --> B["/users"]
A --> C["/users/{id}"]
A --> D["/users/{id}/orders"]
A --> E["/orders"]
B --> F[Collection Resource]
C --> G[Individual Resource]
D --> H[Sub-resource]
E --> I[Related Collection]REST Architecture Principles
The Six Constraints of REST
1. Client-Server Architecture
graph LR
subgraph Client
A[User Interface]
B[Business Logic]
end
subgraph Server
C[Data Storage]
D[Processing Logic]
E[Resource Management]
end
A -.->|HTTP Requests| C
B -.->|HTTP Requests| D
Client -.->|HTTP Requests| E2. Statelessness
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: Each request contains all necessary information
C->>S: GET /users/123 (with auth token)
S->>C: User data
C->>S: PUT /users/123 (with auth token + data)
S->>C: Updated user
Note over S: Server doesn't store client state3. Cacheability
graph TB
A[Client] --> B[Cache Layer]
B --> C[API Server]
B -.->|Cache Hit| A
B -->|Cache Miss| C
C -->|Response + Cache Headers| B4. Uniform Interface
Standard HTTP Methods:
GET: Retrieve resourcesPOST: Create new resourcesPUT: Update/replace resourcesPATCH: Partial updatesDELETE: Remove resources
5. Layered System
graph TB
A[Client] --> B[Load Balancer]
B --> C[API Gateway]
C --> D[Authentication Service]
C --> E[Rate Limiting]
C --> F[Microservice 1]
C --> G[Microservice 2]
F --> H[Database 1]
G --> I[Database 2]6. Code on Demand (Optional)
sequenceDiagram
participant C as Client
participant S as Server
C->>S: GET /api/calculator
S->>C: Calculator logic (JavaScript)
Note over C: Client executes downloaded codeAPI Contract Specification
OpenAPI Specification Structure
Complete API Contract Template
openapi: 3.0.0
info:
title: Microservice API Template
description: Standardized API contract for microservices
version: 1.0.0
contact:
name: API Support
email: api-support@company.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.company.com/v1
description: Production server
- url: https://staging-api.company.com/v1
description: Staging server
paths:
/health:
get:
summary: Health check endpoint
operationId: healthCheck
tags:
- Health
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
$ref: '#/components/schemas/HealthStatus'
/users:
get:
summary: List users
operationId: listUsers
tags:
- Users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
minimum: 1
- name: limit
in: query
schema:
type: integer
default: 20
minimum: 1
maximum: 100
- name: sort
in: query
schema:
type: string
enum: [created_at, updated_at, name]
default: created_at
responses:
'200':
description: List of users
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
post:
summary: Create a new user
operationId: createUser
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'409':
$ref: '#/components/responses/Conflict'
/users/{userId}:
get:
summary: Get user by ID
operationId: getUserById
tags:
- Users
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: User details
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
put:
summary: Update user
operationId: updateUser
tags:
- Users
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: User updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
delete:
summary: Delete user
operationId: deleteUser
tags:
- Users
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: User deleted successfully
'404':
$ref: '#/components/responses/NotFound'
components:
schemas:
HealthStatus:
type: object
properties:
status:
type: string
enum: [healthy, unhealthy]
timestamp:
type: string
format: date-time
version:
type: string
dependencies:
type: array
items:
$ref: '#/components/schemas/DependencyStatus'
DependencyStatus:
type: object
properties:
name:
type: string
status:
type: string
enum: [healthy, unhealthy]
response_time_ms:
type: number
User:
type: object
required:
- id
- email
- name
properties:
id:
type: string
format: uuid
readOnly: true
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
created_at:
type: string
format: date-time
readOnly: true
updated_at:
type: string
format: date-time
readOnly: true
CreateUserRequest:
type: object
required:
- email
- name
properties:
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
UpdateUserRequest:
type: object
properties:
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
UserList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
Pagination:
type: object
properties:
page:
type: integer
minimum: 1
limit:
type: integer
minimum: 1
total:
type: integer
minimum: 0
total_pages:
type: integer
minimum: 0
Error:
type: object
required:
- code
- message
properties:
code:
type: string
message:
type: string
details:
type: array
items:
type: object
properties:
field:
type: string
message:
type: string
trace_id:
type: string
responses:
BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Conflict:
description: Resource conflict
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
InternalServerError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- BearerAuth: []YAMLHTTP Methods and Status Codes
HTTP Methods Matrix
graph TB
subgraph "CRUD Operations"
A[CREATE] --> B[POST]
C[READ] --> D[GET]
E[UPDATE] --> F[PUT/PATCH]
G[DELETE] --> H[DELETE]
end
subgraph "Safety & Idempotency"
B --> I[Not Safe, Not Idempotent]
D --> J[Safe, Idempotent]
F --> K[Not Safe, Idempotent]
H --> L[Not Safe, Idempotent]
endStandard HTTP Status Codes
2xx Success Responses
200 OK: Standard success response201 Created: Resource created successfully202 Accepted: Request accepted for processing204 No Content: Success with no response body
4xx Client Errors
400 Bad Request: Invalid request syntax401 Unauthorized: Authentication required403 Forbidden: Access denied404 Not Found: Resource not found409 Conflict: Resource conflict422 Unprocessable Entity: Validation errors429 Too Many Requests: Rate limit exceeded
5xx Server Errors
500 Internal Server Error: Generic server error502 Bad Gateway: Invalid response from upstream503 Service Unavailable: Service temporarily down504 Gateway Timeout: Upstream timeout
Response Status Code Decision Tree
flowchart TD
A[Request Received] --> B{Valid Request?}
B -->|No| C[400 Bad Request]
B -->|Yes| D{Authenticated?}
D -->|No| E[401 Unauthorized]
D -->|Yes| F{Authorized?}
F -->|No| G[403 Forbidden]
F -->|Yes| H{Resource Exists?}
H -->|No| I[404 Not Found]
H -->|Yes| J{Processing Success?}
J -->|No| K[500 Internal Error]
J -->|Yes| L{Response Body?}
L -->|No| M[204 No Content]
L -->|Yes| N[200 OK]URL Design and Resource Naming
URL Structure Template
https://{domain}/{api-prefix}/{version}/{resource-path}HTTPExample:
https://api.company.com/v1/users/123/orders/456HTTPResource Naming Conventions
1. Use Nouns, Not Verbs
✅ Good:
GET /users
POST /users
GET /users/123
PUT /users/123
DELETE /users/123HTTP❌ Bad:
GET /getUsers
POST /createUser
GET /getUserById/123HTTP2. Use Plural Nouns for Collections
✅ Good:
GET /users # Get all users
GET /orders # Get all orders
GET /products # Get all productsHTTP❌ Bad:
GET /user # Ambiguous
GET /order # Unclear if single or multipleHTTP3. Hierarchical Resource Relationships
graph TB
A["/users"] --> B["/users/{userId}"]
B --> C["/users/{userId}/orders"]
C --> D["/users/{userId}/orders/{orderId}"]
E["/orders"] --> F["/orders/{orderId}"]
F --> G["/orders/{orderId}/items"]
G --> H["/orders/{orderId}/items/{itemId}"]4. Query Parameters for Filtering and Pagination
GET /users?page=1&limit=20&sort=created_at&filter=active
GET /products?category=electronics&price_min=100&price_max=1000
GET /orders?status=pending&created_after=2023-01-01HTTPURL Pattern Examples
Resource Operations
# Collection operations
GET /users # List users
POST /users # Create user
# Individual resource operations
GET /users/{id} # Get specific user
PUT /users/{id} # Update user (full replacement)
PATCH /users/{id} # Partial update user
DELETE /users/{id} # Delete user
# Sub-resource operations
GET /users/{id}/orders # Get user's orders
POST /users/{id}/orders # Create order for user
GET /users/{id}/orders/{oid} # Get specific order for userHTTPSpecial Endpoints
# Health checks
GET /health
GET /health/deep
# Search operations
GET /search/users?q=john
POST /search/advanced
# Bulk operations
POST /users/bulk
PUT /users/bulk
DELETE /users/bulkHTTPRequest and Response Formats
Standard Request Structure
JSON Request Format
{
"data": {
"type": "user",
"attributes": {
"name": "John Doe",
"email": "john@example.com"
},
"relationships": {
"organization": {
"data": {
"type": "organization",
"id": "123"
}
}
}
}
}JSONRequest Headers
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
X-API-Version: 1.0HTTPStandard Response Structure
Success Response Template
{
"success": true,
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "John Doe",
"email": "john@example.com",
"created_at": "2023-01-15T10:30:00Z",
"updated_at": "2023-01-15T10:30:00Z"
}
},
"meta": {
"timestamp": "2023-01-15T10:30:00Z",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}JSONCollection Response Template
{
"success": true,
"data": [
{
"id": "123",
"type": "user",
"attributes": {
"name": "John Doe",
"email": "john@example.com"
}
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"total_pages": 5
},
"timestamp": "2023-01-15T10:30:00Z",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}JSONError Response Template
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "name",
"message": "Name is required"
}
]
},
"meta": {
"timestamp": "2023-01-15T10:30:00Z",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}JSONResponse Headers
Content-Type: application/json
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1640995200
Cache-Control: no-cacheHTTPError Handling Patterns
Error Classification
graph TB
A[API Errors] --> B[Client Errors 4xx]
A --> C[Server Errors 5xx]
B --> D[400 Bad Request]
B --> E[401 Unauthorized]
B --> F[403 Forbidden]
B --> G[404 Not Found]
B --> H[409 Conflict]
B --> I[422 Validation Error]
B --> J[429 Rate Limited]
C --> K[500 Internal Error]
C --> L[502 Bad Gateway]
C --> M[503 Service Unavailable]
C --> N[504 Gateway Timeout]Error Response Standards
Error Code Taxonomy
{
"error": {
"code": "DOMAIN_SPECIFIC_ERROR_CODE",
"category": "validation|authentication|authorization|business|system",
"message": "Human-readable error message",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
}
],
"documentation_url": "https://docs.api.com/errors/DOMAIN_SPECIFIC_ERROR_CODE"
}
}JSONCommon Error Patterns
Validation Errors (422):
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"category": "validation",
"message": "One or more fields contain invalid data",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
},
{
"field": "age",
"code": "OUT_OF_RANGE",
"message": "Age must be between 18 and 120"
}
]
}
}JSONBusiness Logic Errors (409):
{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"category": "business",
"message": "Insufficient account balance for this transaction",
"details": [
{
"field": "amount",
"code": "EXCEEDS_BALANCE",
"message": "Transaction amount exceeds available balance"
}
]
}
}JSONSystem Errors (500):
{
"success": false,
"error": {
"code": "INTERNAL_SERVER_ERROR",
"category": "system",
"message": "An unexpected error occurred. Please try again later.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}
}JSONError Handling Flow
sequenceDiagram
participant C as Client
participant API as API Gateway
participant S as Service
participant L as Logger
C->>API: Request with invalid data
API->>S: Forward request
S->>S: Validate request
S->>L: Log validation error
S->>API: 422 Validation Error Response
API->>C: Formatted error response
Note over C: Client handles error gracefullyAuthentication and Authorization
Authentication Patterns
1. JWT Bearer Token Authentication
sequenceDiagram
participant C as Client
participant Auth as Auth Service
participant API as API Service
C->>Auth: POST /auth/login (credentials)
Auth->>Auth: Validate credentials
Auth->>C: JWT Token
C->>API: GET /users (Bearer token)
API->>API: Validate JWT
API->>C: Protected resourceJWT Token Structure:
{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"sub": "user-123",
"iat": 1640995200,
"exp": 1641081600,
"aud": "api.company.com",
"iss": "auth.company.com",
"roles": ["user", "admin"],
"permissions": ["read:users", "write:users"]
}
}JSON2. API Key Authentication
GET /api/v1/users
Authorization: Bearer sk_live_51H123456789...
X-API-Key: your-api-key-hereHTTP3. OAuth 2.0 Flow
sequenceDiagram
participant C as Client
participant AS as Authorization Server
participant RS as Resource Server
C->>AS: Authorization Request
AS->>C: Authorization Code
C->>AS: Exchange Code for Token
AS->>C: Access Token
C->>RS: Request with Access Token
RS->>AS: Validate Token
AS->>RS: Token Valid
RS->>C: Protected ResourceAuthorization Patterns
Role-Based Access Control (RBAC)
graph TB
A[User] --> B[Role Assignment]
B --> C[Admin Role]
B --> D[User Role]
B --> E[Guest Role]
C --> F[All Permissions]
D --> G[User Permissions]
E --> H[Read-Only Permissions]
F --> I[read:users, write:users, delete:users]
G --> J[read:users, write:own-profile]
H --> K[read:public-data]Attribute-Based Access Control (ABAC)
{
"policy": {
"effect": "Allow",
"action": "read",
"resource": "/users/*",
"condition": {
"and": [
{
"equals": {
"subject.department": "HR"
}
},
{
"equals": {
"resource.confidential": false
}
}
]
}
}
}JSONSecurity Headers
# Response headers for security
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self'HTTPVersioning Strategies
Versioning Approaches Comparison
graph TB
A[API Versioning] --> B[URL Versioning]
A --> C[Header Versioning]
A --> D[Query Parameter Versioning]
A --> E[Content Negotiation]
B --> F["/v1/users, /v2/users"]
C --> G[Accept: application/vnd.api+json;version=1]
D --> H["/users?version=1"]
E --> I[Accept: application/vnd.company.v1+json]1. URL Path Versioning (Recommended)
GET /api/v1/users
GET /api/v2/users
GET /api/v3/usersHTTPPros:
- Clear and explicit
- Easy to understand
- Simple to implement
- Good for caching
Cons:
- URL pollution
- Resource duplication
2. Header Versioning
GET /api/users
Accept: application/vnd.company.v1+json
API-Version: 1.0HTTPPros:
- Clean URLs
- Flexible version negotiation
Cons:
- Less visible
- Caching complexity
3. Query Parameter Versioning
GET /api/users?version=1
GET /api/users?v=2.1HTTPPros:
- Simple to implement
- Optional versioning
Cons:
- Easy to forget
- URL pollution
Version Lifecycle Management
gantt
title API Version Lifecycle
dateFormat YYYY-MM-DD
section v1.0
Development :active, dev1, 2023-01-01, 2023-03-01
Production :prod1, 2023-03-01, 2024-03-01
Deprecated :dep1, 2024-03-01, 2024-09-01
Sunset :sunset1, 2024-09-01, 2024-09-01
section v2.0
Development :dev2, 2023-11-01, 2024-02-01
Production :active, prod2, 2024-02-01, 2025-02-01
section v3.0
Development :dev3, 2024-10-01, 2025-01-01Backward Compatibility Guidelines
Compatible Changes ✅
- Adding new optional fields
- Adding new endpoints
- Adding new optional query parameters
- Adding new HTTP methods to existing endpoints
- Relaxing validation rules
Breaking Changes ❌
- Removing fields
- Renaming fields
- Changing field types
- Making optional fields required
- Changing HTTP status codes
- Removing endpoints
Deprecation Strategy
# OpenAPI deprecation example
paths:
/v1/users:
get:
deprecated: true
summary: Get users (DEPRECATED - use /v2/users)
description: |
This endpoint is deprecated and will be removed on 2024-09-01.
Please migrate to /v2/users.
responses:
'200':
headers:
Sunset:
description: Deprecation date
schema:
type: string
example: "Tue, 01 Sep 2024 00:00:00 GMT"
Deprecation:
description: Deprecation warning
schema:
type: string
example: "true"YAMLMicroservice Communication Patterns
Service Communication Overview
graph TB
subgraph "Synchronous Communication"
A[HTTP/REST] --> B[Direct Service Calls]
A --> C[API Gateway]
A --> D[Service Mesh]
end
subgraph "Asynchronous Communication"
E[Message Queues] --> F[Event Streaming]
E --> G[Pub/Sub]
E --> H[Event Sourcing]
end
B --> I[Request/Response]
F --> J[Event-Driven]Synchronous Patterns
1. Direct Service-to-Service Communication
sequenceDiagram
participant C as Client
participant US as User Service
participant OS as Order Service
participant PS as Payment Service
C->>US: GET /users/123
US->>C: User data
C->>OS: POST /orders
OS->>US: GET /users/123 (validate user)
US->>OS: User data
OS->>PS: POST /payments
PS->>OS: Payment confirmed
OS->>C: Order created2. API Gateway Pattern
graph TB
A[Client] --> B[API Gateway]
B --> C[Authentication Service]
B --> D[User Service]
B --> E[Order Service]
B --> F[Payment Service]
B --> G[Rate Limiting]
B --> H[Load Balancing]
B --> I[Request Routing]
B --> J[Response Aggregation]Asynchronous Patterns
1. Event-Driven Architecture
sequenceDiagram
participant US as User Service
participant EB as Event Bus
participant ES as Email Service
participant AS as Analytics Service
US->>EB: UserRegistered Event
EB->>ES: UserRegistered Event
EB->>AS: UserRegistered Event
ES->>ES: Send welcome email
AS->>AS: Update user metrics2. Saga Pattern for Distributed Transactions
sequenceDiagram
participant O as Order Service
participant P as Payment Service
participant I as Inventory Service
participant S as Shipping Service
Note over O,S: Choreography-based Saga
O->>O: Create Order
O->>P: OrderCreated Event
P->>P: Process Payment
P->>I: PaymentProcessed Event
I->>I: Reserve Inventory
I->>S: InventoryReserved Event
S->>S: Schedule Shipping
S->>O: ShippingScheduled Event
Note over O: Order CompleteCircuit Breaker Pattern
stateDiagram-v2
[*] --> Closed
Closed --> Open : Failure threshold reached
Open --> HalfOpen : Timeout expired
HalfOpen --> Closed : Success
HalfOpen --> Open : Failure
note right of Closed : Normal operation
note right of Open : All requests fail fast
note right of HalfOpen : Limited requests allowedCircuit Breaker Implementation Template
# Circuit breaker configuration
circuit_breaker:
failure_threshold: 5 # Number of failures to open circuit
timeout: 60s # Time to wait before trying again
success_threshold: 3 # Successes needed to close circuit
monitoring_window: 30s # Time window for failure countingYAMLAPI Gateway Patterns
API Gateway Architecture
graph TB
subgraph "External"
A[Web App]
B[Mobile App]
C[Third Party]
end
subgraph "API Gateway Layer"
D[Load Balancer]
E[API Gateway 1]
F[API Gateway 2]
G[API Gateway 3]
end
subgraph "Gateway Features"
H[Authentication]
I[Rate Limiting]
J[Request Routing]
K[Response Caching]
L[Request/Response Transformation]
M[Monitoring & Logging]
end
subgraph "Backend Services"
N[User Service]
O[Order Service]
P[Payment Service]
Q[Notification Service]
end
A --> D
B --> D
C --> D
D --> E
D --> F
D --> G
E --> H
E --> I
E --> J
E --> K
E --> L
E --> M
E --> N
E --> O
E --> P
E --> QGateway Responsibilities
1. Request Routing
# Kong/Nginx routing configuration example
routes:
- name: user-service-route
paths: ["/api/v1/users"]
service: user-service
- name: order-service-route
paths: ["/api/v1/orders"]
service: order-service
- name: payment-service-route
paths: ["/api/v1/payments"]
service: payment-serviceYAML2. Authentication & Authorization
# JWT authentication plugin
plugins:
- name: jwt
config:
key_claim_name: kid
secret_is_base64: false
- name: acl
config:
whitelist: ["admin", "user"]YAML3. Rate Limiting
# Rate limiting configuration
plugins:
- name: rate-limiting
config:
minute: 100
hour: 1000
day: 10000
policy: redis
redis_host: redis.company.comYAML4. Request/Response Transformation
# Transform request/response
plugins:
- name: request-transformer
config:
add:
headers: ["X-API-Version:1.0"]
querystring: ["version:1"]
remove:
headers: ["X-Internal-Header"]
- name: response-transformer
config:
add:
headers: ["X-Response-Time:${latency}"]YAMLBackend for Frontend (BFF) Pattern
graph TB
A[Web App] --> B[Web BFF]
C[Mobile App] --> D[Mobile BFF]
E[Admin Panel] --> F[Admin BFF]
B --> G[User Service]
B --> H[Order Service]
B --> I[Analytics Service]
D --> G
D --> H
D --> J[Push Notification Service]
F --> G
F --> H
F --> K[Admin Service]
F --> L[Reporting Service]Testing Strategies
Testing Pyramid for APIs
graph TB
subgraph "Testing Pyramid"
A[End-to-End Tests]
B[Integration Tests]
C[Unit Tests]
end
subgraph "API Testing Focus"
D[Contract Tests]
E[Performance Tests]
F[Security Tests]
end
A --> G[Few, Slow, Expensive]
B --> H[Medium, Moderate, Moderate]
C --> I[Many, Fast, Cheap]
style C fill:#90EE90
style B fill:#FFE4B5
style A fill:#FFB6C1Contract Testing
Consumer-Driven Contract Testing
sequenceDiagram
participant Consumer as Consumer Team
participant Broker as Pact Broker
participant Provider as Provider Team
Consumer->>Broker: Publish Contract
Provider->>Broker: Fetch Contract
Provider->>Provider: Verify Against Contract
Provider->>Broker: Publish Verification Results
Consumer->>Broker: Check Verification StatusPact Contract Example
{
"consumer": {
"name": "UserService"
},
"provider": {
"name": "OrderService"
},
"interactions": [
{
"description": "Get user orders",
"request": {
"method": "GET",
"path": "/users/123/orders",
"headers": {
"Authorization": "Bearer token"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"data": [
{
"id": "order-123",
"amount": 100.00,
"status": "pending"
}
]
}
}
}
]
}JSONAPI Test Categories
1. Functional Testing
# REST API test scenarios
scenarios:
- name: Create User Happy Path
request:
method: POST
url: /api/v1/users
headers:
Content-Type: application/json
body:
name: "John Doe"
email: "john@example.com"
expect:
status: 201
body:
id: ${generated_uuid}
name: "John Doe"
email: "john@example.com"
- name: Create User with Invalid Email
request:
method: POST
url: /api/v1/users
body:
name: "John Doe"
email: "invalid-email"
expect:
status: 422
body:
error:
code: "VALIDATION_ERROR"
details:
- field: "email"
message: "Invalid email format"YAML2. Performance Testing
# Load testing configuration
load_test:
scenarios:
- name: "User API Load Test"
executor: "ramping-vus"
options:
stages:
- duration: "2m"
target: 10
- duration: "5m"
target: 50
- duration: "2m"
target: 0
thresholds:
http_req_duration:
- "p(95)<500" # 95% of requests under 500ms
http_req_failed:
- "rate<0.01" # Error rate under 1%YAML3. Security Testing
# Security test scenarios
security_tests:
authentication:
- test: "Access without token"
request:
method: GET
url: /api/v1/users
expect:
status: 401
- test: "Access with invalid token"
request:
method: GET
url: /api/v1/users
headers:
Authorization: "Bearer invalid-token"
expect:
status: 401
authorization:
- test: "User accessing admin endpoint"
request:
method: GET
url: /api/v1/admin/users
headers:
Authorization: "Bearer ${user_token}"
expect:
status: 403
input_validation:
- test: "SQL injection attempt"
request:
method: GET
url: "/api/v1/users?id=1' OR '1'='1"
expect:
status: 400YAMLTest Data Management
graph TB
A[Test Data Strategy] --> B[Test Fixtures]
A --> C[Data Factories]
A --> D[Database Seeding]
A --> E[Mocking/Stubbing]
B --> F[Static JSON Files]
C --> G[Dynamic Data Generation]
D --> H[Known Test Data Sets]
E --> I[Simulated Dependencies]Performance and Scalability
Performance Metrics
graph TB
A[API Performance Metrics] --> B[Response Time]
A --> C[Throughput]
A --> D[Availability]
A --> E[Error Rate]
B --> F[P50, P95, P99 Percentiles]
C --> G[Requests per Second]
D --> H[Uptime Percentage]
E --> I[Error Percentage]
F --> J[Target: < 200ms P95]
G --> K[Target: 1000 RPS]
H --> L[Target: 99.9% Uptime]
I --> M[Target: < 0.1% Errors]Caching Strategies
1. HTTP Caching
# Response with caching headers
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
Expires: Thu, 22 Oct 2023 07:28:00 GMTHTTP2. Application-Level Caching
sequenceDiagram
participant C as Client
participant API as API Server
participant Cache as Redis Cache
participant DB as Database
C->>API: GET /users/123
API->>Cache: Check cache
Cache-->>API: Cache miss
API->>DB: Query user
DB->>API: User data
API->>Cache: Store in cache
API->>C: User data
Note over Cache: TTL: 1 hour
C->>API: GET /users/123 (subsequent request)
API->>Cache: Check cache
Cache->>API: Cache hit
API->>C: Cached user data3. CDN Caching
graph TB
A[Client] --> B[CDN Edge Server]
B --> C[Origin API Server]
B --> D[Cached Static Assets]
B --> E[Cached API Responses]
D --> F[Images, CSS, JS]
E --> G[Public API Data]Pagination Strategies
1. Offset-Based Pagination
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 1000,
"total_pages": 50,
"has_next": true,
"has_previous": false
},
"links": {
"first": "/users?page=1&limit=20",
"next": "/users?page=2&limit=20",
"last": "/users?page=50&limit=20"
}
}JSON2. Cursor-Based Pagination
{
"data": [...],
"pagination": {
"has_next": true,
"has_previous": true,
"next_cursor": "eyJpZCI6MTIzfQ==",
"previous_cursor": "eyJpZCI6MTAwfQ=="
},
"links": {
"next": "/users?cursor=eyJpZCI6MTIzfQ==&limit=20",
"previous": "/users?cursor=eyJpZCI6MTAwfQ==&limit=20"
}
}JSONRate Limiting Patterns
1. Token Bucket Algorithm
graph TB
A[Token Bucket] --> B[Capacity: 100 tokens]
A --> C[Refill Rate: 10 tokens/second]
D[Incoming Request] --> E{Tokens Available?}
E -->|Yes| F[Consume Token]
E -->|No| G[Rate Limited]
F --> H[Process Request]
G --> I[429 Too Many Requests]2. Sliding Window Algorithm
gantt
title Rate Limiting - Sliding Window
dateFormat HH:mm:ss
section Window 1
Requests (8/10) :active, w1, 10:00:00, 10:01:00
section Window 2
Requests (6/10) :w2, 10:00:30, 10:01:30
section Window 3
Requests (9/10) :w3, 10:01:00, 10:02:00Horizontal Scaling Patterns
1. Load Balancing
graph TB
A[Load Balancer] --> B[API Instance 1]
A --> C[API Instance 2]
A --> D[API Instance 3]
B --> E[Database Cluster]
C --> E
D --> E
A --> F[Health Checks]
A --> G[Request Distribution]
F --> H[Round Robin]
F --> I[Least Connections]
F --> J[Weighted Round Robin]2. Database Scaling
graph TB
subgraph "Read Replicas"
A[Primary Database]
B[Read Replica 1]
C[Read Replica 2]
D[Read Replica 3]
end
subgraph "API Instances"
E[API Instance 1]
F[API Instance 2]
G[API Instance 3]
end
E -->|Write| A
F -->|Write| A
G -->|Write| A
E -->|Read| B
F -->|Read| C
G -->|Read| D
A -.->|Replication| B
A -.->|Replication| C
A -.->|Replication| DMonitoring and Observability
Three Pillars of Observability
graph TB
A[Observability] --> B[Metrics]
A --> C[Logs]
A --> D[Traces]
B --> E[System Health]
B --> F[Performance KPIs]
B --> G[Business Metrics]
C --> H[Application Logs]
C --> I[Access Logs]
C --> J[Error Logs]
D --> K[Request Flow]
D --> L[Latency Analysis]
D --> M[Dependency Mapping]API Metrics Collection
1. RED Metrics
# Prometheus metrics configuration
metrics:
rate:
- name: api_requests_total
type: counter
labels: [method, endpoint, status_code]
errors:
- name: api_errors_total
type: counter
labels: [method, endpoint, error_type]
duration:
- name: api_request_duration_seconds
type: histogram
labels: [method, endpoint]
buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 10]YAML2. USE Metrics
# System resource metrics
system_metrics:
utilization:
- cpu_usage_percent
- memory_usage_percent
- disk_usage_percent
saturation:
- cpu_queue_length
- memory_swap_usage
- disk_io_wait
errors:
- system_errors_total
- disk_errors_total
- network_errors_totalYAMLStructured Logging
Log Format Standard
{
"timestamp": "2023-01-15T10:30:00.123Z",
"level": "INFO",
"service": "user-service",
"version": "1.2.3",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "user-123",
"method": "GET",
"endpoint": "/api/v1/users/123",
"status_code": 200,
"duration_ms": 45,
"message": "User retrieved successfully",
"metadata": {
"user_agent": "Mozilla/5.0...",
"ip_address": "192.168.1.100",
"correlation_id": "business-transaction-456"
}
}JSONDistributed Tracing
OpenTelemetry Trace Example
gantt
title Distributed Trace
dateFormat HH:mm:ss.SSS
section API Gateway
Request Processing :gateway, 10:00:00.000, 10:00:00.100
section User Service
Validate User :user-svc, 10:00:00.020, 10:00:00.080
section Database
User Query :db-query, 10:00:00.030, 10:00:00.070Trace Span Structure
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"parent_span_id": "83887e914dc8abd3",
"operation_name": "GET /users/{id}",
"start_time": "2023-01-15T10:30:00.000Z",
"duration": "45ms",
"tags": {
"http.method": "GET",
"http.url": "/api/v1/users/123",
"http.status_code": 200,
"user.id": "123",
"component": "user-service"
},
"logs": [
{
"timestamp": "2023-01-15T10:30:00.020Z",
"fields": {
"event": "db.query.start",
"query": "SELECT * FROM users WHERE id = ?"
}
}
]
}JSONHealth Check Endpoints
Health Check Implementation
# Health check endpoint specification
/health:
get:
summary: Basic health check
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [healthy, unhealthy]
timestamp:
type: string
format: date-time
/health/deep:
get:
summary: Deep health check with dependencies
responses:
'200':
description: Detailed health status
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [healthy, degraded, unhealthy]
checks:
type: object
properties:
database:
$ref: '#/components/schemas/HealthCheck'
redis:
$ref: '#/components/schemas/HealthCheck'
external_api:
$ref: '#/components/schemas/HealthCheck'YAMLAlerting Strategy
graph TB
A[Metrics Collection] --> B[Alert Rules]
B --> C[Alert Manager]
C --> D[Notification Channels]
D --> E[PagerDuty]
D --> F[Slack]
D --> G[Email]
D --> H[SMS]
B --> I[SLI Violations]
B --> J[Error Rate Spikes]
B --> K[Latency Increases]
B --> L[Availability Issues]Security Best Practices
API Security Checklist
1. Authentication & Authorization
graph TB
A[API Security] --> B[Authentication]
A --> C[Authorization]
A --> D[Input Validation]
A --> E[Rate Limiting]
A --> F[HTTPS/TLS]
A --> G[Security Headers]
B --> H[JWT Tokens]
B --> I[OAuth 2.0]
B --> J[API Keys]
C --> K[RBAC]
C --> L[ABAC]
C --> M[Scope-based Access]2. Input Validation & Sanitization
# Input validation rules
validation_rules:
user_creation:
email:
type: string
format: email
max_length: 255
required: true
name:
type: string
min_length: 1
max_length: 100
pattern: "^[a-zA-Z\\s]+$"
required: true
age:
type: integer
minimum: 18
maximum: 120
sanitization:
- html_escape: true
- sql_injection_prevention: true
- xss_protection: trueYAML3. OWASP API Security Top 10
graph TB
A[OWASP API Top 10] --> B[A1: Broken Object Level Authorization]
A --> C[A2: Broken User Authentication]
A --> D[A3: Excessive Data Exposure]
A --> E[A4: Lack of Resources & Rate Limiting]
A --> F[A5: Broken Function Level Authorization]
A --> G[A6: Mass Assignment]
A --> H[A7: Security Misconfiguration]
A --> I[A8: Injection]
A --> J[A9: Improper Assets Management]
A --> K[A10: Insufficient Logging & Monitoring]Security Headers
# Security headers for API responses
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()HTTPAPI Key Management
1. API Key Structure
# API Key format
ak_live_1234567890abcdef1234567890abcdef12345678
│ │ │
│ │ └─ Random key material (32 chars)
│ └─ Environment (live/test)
└─ Prefix (ak = api key)Crystal2. Key Rotation Strategy
sequenceDiagram
participant C as Client
participant KM as Key Management
participant API as API Service
Note over KM: Key rotation initiated
KM->>KM: Generate new key
KM->>C: New key issued
KM->>API: Update key whitelist
Note over C,API: Grace period (both keys valid)
C->>API: Request with new key
API->>API: Validate new key
API->>C: Success response
Note over KM: Old key expires
KM->>API: Remove old key from whitelistData Protection
1. Encryption in Transit
# TLS configuration
tls_config:
min_version: "1.2"
max_version: "1.3"
cipher_suites:
- "TLS_AES_256_GCM_SHA384"
- "TLS_AES_128_GCM_SHA256"
- "TLS_CHACHA20_POLY1305_SHA256"
certificates:
- cert_file: "/path/to/cert.pem"
key_file: "/path/to/key.pem"YAML2. Encryption at Rest
# Database encryption
database:
encryption:
algorithm: "AES-256-GCM"
key_management: "AWS KMS"
encrypted_fields:
- "email"
- "phone_number"
- "ssn"
- "credit_card_number"YAML3. PII Data Handling
{
"user": {
"id": "user-123",
"name": "John Doe",
"email": "j***@example.com",
"phone": "***-***-1234",
"created_at": "2023-01-15T10:30:00Z"
},
"data_classification": {
"email": "PII",
"phone": "PII",
"name": "PII"
}
}JSONDocumentation Standards
API Documentation Structure
1. OpenAPI Documentation Template
openapi: 3.0.0
info:
title: Company API
description: |
# Introduction
Welcome to the Company API documentation. This API provides access to user management, order processing, and payment functionality.
## Authentication
All API endpoints require authentication using Bearer tokens:
```
Authorization: Bearer your-jwt-token-here
```
## Rate Limiting
API requests are limited to 1000 requests per hour per authenticated user.
## Error Handling
All errors follow RFC 7807 Problem Details standard.
## Versioning
This API uses URL versioning (e.g., `/v1/`, `/v2/`).
version: 1.0.0
termsOfService: https://company.com/terms
contact:
name: API Support Team
email: api-support@company.com
url: https://company.com/support
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.company.com/v1
description: Production server
- url: https://staging-api.company.com/v1
description: Staging server
tags:
- name: Authentication
description: User authentication and token management
- name: Users
description: User account management
- name: Orders
description: Order processing and managementYAML2. Interactive Documentation
graph TB
A[API Documentation] --> B[Interactive Examples]
A --> C[Code Samples]
A --> D[Schema Validation]
A --> E[Try It Out Feature]
B --> F[Swagger UI]
B --> G[Redoc]
B --> H[Postman Collections]
C --> I[cURL Examples]
C --> J[SDK Examples]
C --> K[Language-specific Samples]Documentation Best Practices
1. Interactive Playground
graph TB
A[API Documentation] --> B[Interactive Playground]
B --> C[Try It Out]
B --> D[Code Generation]
B --> E[Response Examples]
C --> F[Live API Calls]
D --> G[Multiple Languages]
E --> H[Success/Error Scenarios]2. Version Management
# Documentation versioning strategy
documentation:
versions:
- version: "1.0"
status: "deprecated"
sunset_date: "2024-09-01"
migration_guide: "/docs/migration/v1-to-v2"
- version: "2.0"
status: "current"
changelog: "/docs/changelog/v2"
- version: "3.0"
status: "beta"
preview_access: trueYAMLImplementation Examples
FastAPI Implementation Example
# FastAPI microservice implementation
from fastapi import FastAPI, HTTPException, Depends, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field, EmailStr
from typing import Optional, List
import uuid
from datetime import datetime
import logging
# Configure structured logging
logging.basicConfig(
level=logging.INFO,
format='{"timestamp":"%(asctime)s","level":"%(levelname)s","message":"%(message)s"}'
)
logger = logging.getLogger(__name__)
app = FastAPI(
title="User Management API",
description="Standardized microservice for user management",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Security
security = HTTPBearer()
# Pydantic models
class UserBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
class UserCreate(UserBase):
pass
class UserUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=100)
email: Optional[EmailStr] = None
class User(UserBase):
id: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class UserList(BaseModel):
data: List[User]
pagination: dict
class ErrorDetail(BaseModel):
field: str
message: str
class APIError(BaseModel):
code: str
message: str
details: Optional[List[ErrorDetail]] = None
trace_id: Optional[str] = None
class HealthStatus(BaseModel):
status: str
timestamp: datetime
version: str
dependencies: List[dict] = []
# Dependency injection
async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
# JWT validation logic here
token = credentials.credentials
# Validate token and return user
return {"user_id": "user-123", "roles": ["user"]}
# Health check endpoints
@app.get("/health", response_model=HealthStatus, tags=["Health"])
async def health_check():
"""Basic health check endpoint"""
return HealthStatus(
status="healthy",
timestamp=datetime.utcnow(),
version="1.0.0"
)
@app.get("/health/deep", response_model=HealthStatus, tags=["Health"])
async def deep_health_check():
"""Deep health check with dependency validation"""
dependencies = []
# Check database connection
try:
# Database health check logic
dependencies.append({
"name": "database",
"status": "healthy",
"response_time_ms": 15
})
except Exception as e:
dependencies.append({
"name": "database",
"status": "unhealthy",
"error": str(e)
})
overall_status = "healthy" if all(d["status"] == "healthy" for d in dependencies) else "unhealthy"
return HealthStatus(
status=overall_status,
timestamp=datetime.utcnow(),
version="1.0.0",
dependencies=dependencies
)
# User endpoints
@app.get("/users", response_model=UserList, tags=["Users"])
async def list_users(
page: int = 1,
limit: int = 20,
sort: str = "created_at",
current_user: dict = Depends(get_current_user)
):
"""List users with pagination"""
# Implementation logic
users = [] # Fetch from database
total = 0 # Get total count
return UserList(
data=users,
pagination={
"page": page,
"limit": limit,
"total": total,
"total_pages": (total + limit - 1) // limit
}
)
@app.post("/users", response_model=User, status_code=status.HTTP_201_CREATED, tags=["Users"])
async def create_user(
user_data: UserCreate,
current_user: dict = Depends(get_current_user)
):
"""Create a new user"""
try:
# Check if user already exists
# existing_user = get_user_by_email(user_data.email)
# if existing_user:
# raise HTTPException(
# status_code=status.HTTP_409_CONFLICT,
# detail={"code": "USER_EXISTS", "message": "User with this email already exists"}
# )
# Create user
user = User(
id=str(uuid.uuid4()),
name=user_data.name,
email=user_data.email,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
# Save to database
# save_user(user)
logger.info(f"User created: {user.id}")
return user
except HTTPException:
raise
except Exception as e:
logger.error(f"Error creating user: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={"code": "INTERNAL_ERROR", "message": "An unexpected error occurred"}
)
@app.get("/users/{user_id}", response_model=User, tags=["Users"])
async def get_user(
user_id: str,
current_user: dict = Depends(get_current_user)
):
"""Get user by ID"""
# Fetch user from database
# user = get_user_by_id(user_id)
# if not user:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail={"code": "USER_NOT_FOUND", "message": "User not found"}
# )
# Mock response
user = User(
id=user_id,
name="John Doe",
email="john@example.com",
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
return user
@app.put("/users/{user_id}", response_model=User, tags=["Users"])
async def update_user(
user_id: str,
user_data: UserUpdate,
current_user: dict = Depends(get_current_user)
):
"""Update user"""
# Implementation logic
pass
@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT, tags=["Users"])
async def delete_user(
user_id: str,
current_user: dict = Depends(get_current_user)
):
"""Delete user"""
# Implementation logic
pass
# Exception handlers
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={
"success": False,
"error": exc.detail,
"meta": {
"timestamp": datetime.utcnow().isoformat(),
"request_id": str(uuid.uuid4())
}
}
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)PythonExpress.js Implementation Example
// Express.js microservice implementation
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const { v4: uuidv4 } = require('uuid');
const Joi = require('joi');
const jwt = require('jsonwebtoken');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json({ limit: '10mb' }));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: {
error: {
code: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests, please try again later.'
}
}
});
app.use('/api/', limiter);
// Validation schemas
const userCreateSchema = Joi.object({
name: Joi.string().min(1).max(100).required(),
email: Joi.string().email().required()
});
const userUpdateSchema = Joi.object({
name: Joi.string().min(1).max(100),
email: Joi.string().email()
});
// Authentication middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({
success: false,
error: {
code: 'MISSING_TOKEN',
message: 'Access token is required'
}
});
}
jwt.verify(token, process.env.JWT_SECRET || 'secret', (err, user) => {
if (err) {
return res.status(403).json({
success: false,
error: {
code: 'INVALID_TOKEN',
message: 'Invalid or expired token'
}
});
}
req.user = user;
next();
});
};
// Error handling middleware
const errorHandler = (err, req, res, next) => {
console.error('Error:', err);
const response = {
success: false,
error: {
code: 'INTERNAL_SERVER_ERROR',
message: 'An unexpected error occurred'
},
meta: {
timestamp: new Date().toISOString(),
request_id: uuidv4()
}
};
res.status(500).json(response);
};
// Request logging middleware
app.use((req, res, next) => {
const requestId = uuidv4();
req.requestId = requestId;
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
message: 'Request received',
method: req.method,
url: req.url,
request_id: requestId,
user_agent: req.get('User-Agent'),
ip: req.ip
}));
next();
});
// Health check endpoints
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
version: '1.0.0'
});
});
app.get('/health/deep', async (req, res) => {
const dependencies = [];
// Check database connection
try {
// Database health check logic
dependencies.push({
name: 'database',
status: 'healthy',
response_time_ms: 15
});
} catch (error) {
dependencies.push({
name: 'database',
status: 'unhealthy',
error: error.message
});
}
const overallStatus = dependencies.every(d => d.status === 'healthy') ? 'healthy' : 'unhealthy';
res.json({
status: overallStatus,
timestamp: new Date().toISOString(),
version: '1.0.0',
dependencies
});
});
// User endpoints
app.get('/api/v1/users', authenticateToken, async (req, res) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 20;
const sort = req.query.sort || 'created_at';
// Fetch users from database
const users = []; // Mock data
const total = 0;
res.json({
success: true,
data: users,
meta: {
pagination: {
page,
limit,
total,
total_pages: Math.ceil(total / limit)
},
timestamp: new Date().toISOString(),
request_id: req.requestId
}
});
} catch (error) {
next(error);
}
});
app.post('/api/v1/users', authenticateToken, async (req, res, next) => {
try {
// Validate request body
const { error, value } = userCreateSchema.validate(req.body);
if (error) {
return res.status(422).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request data',
details: error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message
}))
},
meta: {
timestamp: new Date().toISOString(),
request_id: req.requestId
}
});
}
// Create user
const user = {
id: uuidv4(),
name: value.name,
email: value.email,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
};
// Save to database
// await saveUser(user);
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
message: 'User created',
user_id: user.id,
request_id: req.requestId
}));
res.status(201).json({
success: true,
data: user,
meta: {
timestamp: new Date().toISOString(),
request_id: req.requestId
}
});
} catch (error) {
next(error);
}
});
app.get('/api/v1/users/:userId', authenticateToken, async (req, res, next) => {
try {
const { userId } = req.params;
// Fetch user from database
// const user = await getUserById(userId);
// if (!user) {
// return res.status(404).json({
// success: false,
// error: {
// code: 'USER_NOT_FOUND',
// message: 'User not found'
// }
// });
// }
// Mock response
const user = {
id: userId,
name: 'John Doe',
email: 'john@example.com',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
};
res.json({
success: true,
data: user,
meta: {
timestamp: new Date().toISOString(),
request_id: req.requestId
}
});
} catch (error) {
next(error);
}
});
// Error handling
app.use(errorHandler);
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({
success: false,
error: {
code: 'NOT_FOUND',
message: 'The requested resource was not found'
},
meta: {
timestamp: new Date().toISOString(),
request_id: req.requestId || uuidv4()
}
});
});
app.listen(PORT, () => {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
message: `Server running on port ${PORT}`,
service: 'user-service',
version: '1.0.0'
}));
});
module.exports = app;JavaScriptSpring Boot Implementation Example
// Spring Boot microservice implementation
package com.company.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// DTOs
class UserCreateRequest {
@NotBlank(message = "Name is required")
@Size(min = 1, max = 100, message = "Name must be between 1 and 100 characters")
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
private String email;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
class UserResponse {
private String id;
private String name;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Constructors, getters and setters
public UserResponse(String id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// Getters and setters
public String getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
public LocalDateTime getCreatedAt() { return createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
}
class ApiResponse<T> {
private boolean success;
private T data;
private Map<String, Object> meta;
public ApiResponse(boolean success, T data, Map<String, Object> meta) {
this.success = success;
this.data = data;
this.meta = meta;
}
// Getters and setters
public boolean isSuccess() { return success; }
public T getData() { return data; }
public Map<String, Object> getMeta() { return meta; }
}
class HealthStatus {
private String status;
private LocalDateTime timestamp;
private String version;
public HealthStatus(String status, String version) {
this.status = status;
this.timestamp = LocalDateTime.now();
this.version = version;
}
// Getters and setters
public String getStatus() { return status; }
public LocalDateTime getTimestamp() { return timestamp; }
public String getVersion() { return version; }
}
// Controllers
@RestController
@RequestMapping("/health")
@Validated
class HealthController {
@GetMapping
public ResponseEntity<HealthStatus> healthCheck() {
return ResponseEntity.ok(new HealthStatus("healthy", "1.0.0"));
}
@GetMapping("/deep")
public ResponseEntity<Map<String, Object>> deepHealthCheck() {
// Implement deep health check logic
Map<String, Object> response = Map.of(
"status", "healthy",
"timestamp", LocalDateTime.now(),
"version", "1.0.0",
"dependencies", List.of()
);
return ResponseEntity.ok(response);
}
}
@RestController
@RequestMapping("/api/v1/users")
@Validated
class UserController {
@GetMapping
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ApiResponse<List<UserResponse>>> listUsers(
@RequestParam(defaultValue = "1") @Min(1) int page,
@RequestParam(defaultValue = "20") @Min(1) @Max(100) int limit,
@RequestParam(defaultValue = "created_at") String sort) {
// Implementation logic
List<UserResponse> users = List.of(); // Fetch from service
Map<String, Object> meta = Map.of(
"pagination", Map.of(
"page", page,
"limit", limit,
"total", 0,
"total_pages", 0
),
"timestamp", LocalDateTime.now(),
"request_id", UUID.randomUUID().toString()
);
return ResponseEntity.ok(new ApiResponse<>(true, users, meta));
}
@PostMapping
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ApiResponse<UserResponse>> createUser(
@Valid @RequestBody UserCreateRequest request) {
// Create user
UserResponse user = new UserResponse(
UUID.randomUUID().toString(),
request.getName(),
request.getEmail()
);
// Save to database
// userService.save(user);
Map<String, Object> meta = Map.of(
"timestamp", LocalDateTime.now(),
"request_id", UUID.randomUUID().toString()
);
return ResponseEntity.status(HttpStatus.CREATED)
.body(new ApiResponse<>(true, user, meta));
}
@GetMapping("/{userId}")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ApiResponse<UserResponse>> getUser(
@PathVariable String userId) {
// Fetch user from service
UserResponse user = new UserResponse(userId, "John Doe", "john@example.com");
Map<String, Object> meta = Map.of(
"timestamp", LocalDateTime.now(),
"request_id", UUID.randomUUID().toString()
);
return ResponseEntity.ok(new ApiResponse<>(true, user, meta));
}
}JavaDocker Configuration
# Multi-stage Dockerfile for microservice
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-alpine AS runtime
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodeuser -u 1001
WORKDIR /app
# Copy dependencies
COPY --from=builder --chown=nodeuser:nodejs /app/node_modules ./node_modules
COPY --chown=nodeuser:nodejs . .
# Security headers
LABEL \
org.opencontainers.image.title="User Service API" \
org.opencontainers.image.description="Microservice for user management" \
org.opencontainers.image.version="1.0.0" \
org.opencontainers.image.vendor="Company Name"
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Switch to non-root user
USER nodeuser
EXPOSE 3000
CMD ["node", "server.js"]DockerfileKubernetes Deployment
# Kubernetes deployment configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
version: v1
spec:
containers:
- name: user-service
image: company/user-service:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: jwt-secret
key: secret
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: user-service-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
tls:
- hosts:
- api.company.com
secretName: api-tls
rules:
- host: api.company.com
http:
paths:
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80YAMLTerraform Infrastructure
# Terraform configuration for API infrastructure
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# VPC Configuration
resource "aws_vpc" "api_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "api-vpc"
Environment = var.environment
}
}
# Subnets
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.api_vpc.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = var.availability_zones[count.index]
tags = {
Name = "private-subnet-${count.index + 1}"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.api_vpc.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-${count.index + 1}"
}
}
# EKS Cluster
resource "aws_eks_cluster" "api_cluster" {
name = "api-cluster"
role_arn = aws_iam_role.eks_cluster.arn
version = "1.27"
vpc_config {
subnet_ids = concat(aws_subnet.private[*].id, aws_subnet.public[*].id)
endpoint_private_access = true
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster_policy,
]
}
# RDS Database
resource "aws_db_instance" "api_database" {
identifier = "api-database"
engine = "postgres"
engine_version = "15.3"
instance_class = "db.t3.micro"
allocated_storage = 20
max_allocated_storage = 100
storage_type = "gp2"
storage_encrypted = true
db_name = var.database_name
username = var.database_username
password = var.database_password
vpc_security_group_ids = [aws_security_group.rds.id]
db_subnet_group_name = aws_db_subnet_group.api.name
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
skip_final_snapshot = true
deletion_protection = false
tags = {
Name = "api-database"
Environment = var.environment
}
}
# ElastiCache Redis
resource "aws_elasticache_subnet_group" "api" {
name = "api-cache-subnet"
subnet_ids = aws_subnet.private[*].id
}
resource "aws_elasticache_cluster" "api_cache" {
cluster_id = "api-cache"
engine = "redis"
node_type = "cache.t3.micro"
num_cache_nodes = 1
parameter_group_name = "default.redis7"
port = 6379
subnet_group_name = aws_elasticache_subnet_group.api.name
security_group_ids = [aws_security_group.redis.id]
}
# Variables
variable "aws_region" {
description = "AWS region"
default = "us-west-2"
}
variable "environment" {
description = "Environment name"
default = "production"
}
variable "availability_zones" {
description = "Availability zones"
default = ["us-west-2a", "us-west-2b"]
}
variable "database_name" {
description = "Database name"
default = "apidb"
}
variable "database_username" {
description = "Database username"
default = "apiuser"
}
variable "database_password" {
description = "Database password"
sensitive = true
}
# Outputs
output "cluster_endpoint" {
value = aws_eks_cluster.api_cluster.endpoint
}
output "database_endpoint" {
value = aws_db_instance.api_database.endpoint
}
output "redis_endpoint" {
value = aws_elasticache_cluster.api_cache.cache_nodes[0].address
}HCLConclusion
This comprehensive guide has covered all essential aspects of designing and implementing standardized, language-agnostic API endpoint architecture for microservices. The key takeaways include:
Best Practices Summary
mindmap
root(("API Best Practices"))
Contract-First Design
OpenAPI Specification
Language Agnostic
Version Management
RESTful Design
Resource-Oriented URLs
"HTTP Methods & Status Codes"
Stateless Architecture
Security
Authentication & Authorization
Input Validation
Rate Limiting
HTTPS/TLS
Performance
Caching Strategies
Pagination
Load Balancing
Horizontal Scaling
Observability
Structured Logging
Metrics Collection
Distributed Tracing
Health Checks
Testing
Unit Tests
Integration Tests
Contract Tests
Security TestsImplementation Checklist
- ✅ Define OpenAPI contracts first
- ✅ Implement consistent error handling
- ✅ Use standardized response formats
- ✅ Implement proper authentication/authorization
- ✅ Add comprehensive logging and monitoring
- ✅ Include health check endpoints
- ✅ Implement rate limiting
- ✅ Use proper HTTP status codes
- ✅ Document APIs thoroughly
- ✅ Test extensively (unit, integration, contract)
- ✅ Implement caching strategies
- ✅ Plan for scalability
- ✅ Secure all endpoints
- ✅ Version APIs appropriately
Future Considerations
As APIs evolve, consider these emerging patterns:
- GraphQL Integration: For complex data fetching requirements
- Event-Driven APIs: WebSockets and Server-Sent Events
- API Mesh: Advanced service mesh for API management
- ML/AI Integration: APIs for machine learning services
- Serverless APIs: Function-as-a-Service implementations
This guide provides a solid foundation for building robust, scalable, and maintainable API architectures that can adapt to changing business requirements while maintaining consistency and reliability across your microservice ecosystem.
Discover more from Altgr Blog
Subscribe to get the latest posts sent to your email.
