The Four Phases
You MUST complete each phase before proceeding to the next.
Phase 1: Contract & Discovery
BEFORE writing the adapter:
API Audit
- Read the Rate Limits. (Requests per minute?).
- Read the SLA. (Do they guarantee 99.9% uptime?).
- Auth Method: OAuth2? API Key? mTLS?
- Webhooks: Do they push data to us? How do we verify the signature?
Data Mapping (The Anti-Corruption Layer)
- Rule: Never let external data structures leak into your internal domain.
- Create an "Adapter" or "Mapper" to convert their JSON to Your Object.
- If they change their API, you only change the Mapper, not your whole app.
Secret Management
- Where do the API keys live? (Secret Manager).
- Rotation Strategy: What happens if a key leaks?
Phase 2: Resilience & Queuing
Assume they will fail:
Circuit Breaker Pattern
- If the 3rd party fails 5 times, stop calling them for 1 minute.
- Fail fast instead of hanging the user's thread.
Asynchronous Decoupling
- Don't call the 3rd party in the main request loop.
- Put the job on a Queue (SQS/RabbitMQ). "Process Payment" -> Queue.
- Retries: Exponential Backoff (Retry in 1s, then 2s, then 4s).
Idempotency
- What happens if we send the same request twice? (Double charge?).
- Send an "Idempotency Key" (Unique ID) with every write request.
Phase 3: Testing & Mocking
Don't test against production APIs:
Sandboxing
- Use their Sandbox/Test environment.
- Warning: Sandboxes often behave differently than Prod (faster/slower).
Mock Servers (Wiremock)
- Record the real response once. Replay it in your tests.
- Simulate 500 Errors and Timeouts. Does your app handle it?
Consumer Driven Contracts
- Verify that the fields you need actually exist.
Phase 4: Governance & Monitoring
Watching the relationship:
Quota Tracking
- Alert before you hit the rate limit.
- Alert on cost spikes (e.g., Maps API bill usage).
Webhook Verification
- Verify the cryptographic signature of incoming webhooks.
- Prevent "Replay Attacks."
Vendor Updates
- Subscribe to their changelog.
- Deprecation warnings are deadlines, not suggestions.
Red Flags - STOP and Follow Process
If you catch yourself thinking:
- "I'll just call the API directly from the frontend." (Leaked keys).
- "They rarely go down, I don't need a retry loop." (They will go down).
- "I'll handle the webhook later." (Data sync issues).
- "I'll store the API key in the code." (Security breach).
- "If the API is slow, the user just has to wait." (Bad UX).
- "I'll use their data format everywhere in my app." (Tight coupling).
ALL of these mean: STOP. Return to Phase 1.
Quick Reference
| Phase |
Key Activities |
Success Criteria |
| 1. Contract |
Mapping, Auth, Secrets |
Clean internal interface |
| 2. Resilience |
Circuit Breakers, Queues |
No cascading failures |
| 3. Testing |
Mocks, Sandboxes |
Tests pass without internet |
| 4. Governance |
Rate Limits, Cost alerts |
No bill shock, No outages |
🛠️ Modern Middleware Stack (2026)
API Gateway
- Kong: Open-source, plugin ecosystem, rate limiting
- Tyk: Developer portal, analytics
- AWS API Gateway: Managed, serverless
- Envoy Proxy: Cloud-native, used by Istio
Service Mesh (10+ Microservices)
- Istio: Feature-rich, observability, mTLS
- Linkerd: Simpler, Rust-based, lightweight
- Consul Connect: HashiCorp ecosystem
- When: Complex inter-service communication, need per-request metrics
Message Queues
- RabbitMQ: Classic, reliable, AMQP
- Apache Kafka: High-throughput streaming
- AWS SQS: Managed, serverless
- BullMQ: Redis-based, Node.js
Observability (OpenTelemetry)
- Tracing: Jaeger, Tempo, Honeycomb
- Metrics: Prometheus, Datadog
- Logs: Grafana Loki, CloudWatch
📊 OpenTelemetry Integration
Distributed Tracing
const { trace } = require('@opentelemetry/api');
async function callExternalAPI(userId) {
const span = trace.getActiveSpan();
span?.setAttribute('user.id', userId);
span?.addEvent('calling_external_api');
try {
const response = await fetch('https://api.example.com/data');
span?.setAttribute('http.status_code', response.status);
return response.json();
} catch (error) {
span?.recordException(error);
span?.setStatus({ code: SpanStatusCode.ERROR });
throw error;
}
}
Propagating Context
- Trace ID: Flows through all services
- Baggage: Carry metadata (user_id, tenant_id, feature_flags)
- W3C Standard: traceparent header
🔒 Security Patterns
API Key Rotation
1. Generate new key
2. Deploy with both keys accepted (grace period)
3. Update clients to new key
4. Remove old key after 30 days
Webhook Signature Verification
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
Rate Limiting Strategies
- Fixed Window: 100 req/hour (simple, can burst)
- Sliding Window: Smooths out bursts
- Token Bucket: Allows bursts up to capacity
- Leaky Bucket: Constant rate, smoothest
1---2name: middleware-engineer3description: Middleware Engineer Skill4---5## The Four Phases67You MUST complete each phase before proceeding to the next.89### Phase 1: Contract & Discovery1011**BEFORE writing the adapter:**12131. **API Audit**14 - Read the Rate Limits. (Requests per minute?).15 - Read the SLA. (Do they guarantee 99.9% uptime?).16 - **Auth Method:** OAuth2? API Key? mTLS?17 - **Webhooks:** Do they push data to us? How do we verify the signature?18192. **Data Mapping (The Anti-Corruption Layer)**20 - **Rule:** Never let external data structures leak into your internal domain.21 - Create an "Adapter" or "Mapper" to convert their JSON to Your Object.22 - If they change their API, you only change the Mapper, not your whole app.23243. **Secret Management**25 - Where do the API keys live? (Secret Manager).26 - Rotation Strategy: What happens if a key leaks?2728### Phase 2: Resilience & Queuing2930**Assume they will fail:**31321. **Circuit Breaker Pattern**33 - If the 3rd party fails 5 times, stop calling them for 1 minute.34 - Fail fast instead of hanging the user's thread.35362. **Asynchronous Decoupling**37 - Don't call the 3rd party in the main request loop.38 - Put the job on a Queue (SQS/RabbitMQ). "Process Payment" -> Queue.39 - **Retries:** Exponential Backoff (Retry in 1s, then 2s, then 4s).40413. **Idempotency**42 - What happens if we send the same request twice? (Double charge?).43 - Send an "Idempotency Key" (Unique ID) with every write request.4445### Phase 3: Testing & Mocking4647**Don't test against production APIs:**48491. **Sandboxing**50 - Use their Sandbox/Test environment.51 - **Warning:** Sandboxes often behave differently than Prod (faster/slower).52532. **Mock Servers (Wiremock)**54 - Record the real response once. Replay it in your tests.55 - Simulate 500 Errors and Timeouts. Does your app handle it?56573. **Consumer Driven Contracts**58 - Verify that the fields you need actually exist.5960### Phase 4: Governance & Monitoring6162**Watching the relationship:**63641. **Quota Tracking**65 - Alert *before* you hit the rate limit.66 - Alert on cost spikes (e.g., Maps API bill usage).67682. **Webhook Verification**69 - Verify the cryptographic signature of incoming webhooks.70 - Prevent "Replay Attacks."71723. **Vendor Updates**73 - Subscribe to their changelog.74 - Deprecation warnings are deadlines, not suggestions.7576## Red Flags - STOP and Follow Process7778If you catch yourself thinking:79- "I'll just call the API directly from the frontend." (Leaked keys).80- "They rarely go down, I don't need a retry loop." (They will go down).81- "I'll handle the webhook later." (Data sync issues).82- "I'll store the API key in the code." (Security breach).83- "If the API is slow, the user just has to wait." (Bad UX).84- "I'll use their data format everywhere in my app." (Tight coupling).8586**ALL of these mean: STOP. Return to Phase 1.**8788## Quick Reference8990| Phase | Key Activities | Success Criteria |91|-------|---------------|------------------|92| **1. Contract** | Mapping, Auth, Secrets | Clean internal interface |93| **2. Resilience** | Circuit Breakers, Queues | No cascading failures |94| **3. Testing** | Mocks, Sandboxes | Tests pass without internet |95| **4. Governance** | Rate Limits, Cost alerts | No bill shock, No outages |9697## 🛠️ Modern Middleware Stack (2026)9899### API Gateway100- **Kong:** Open-source, plugin ecosystem, rate limiting101- **Tyk:** Developer portal, analytics102- **AWS API Gateway:** Managed, serverless103- **Envoy Proxy:** Cloud-native, used by Istio104105### Service Mesh (10+ Microservices)106- **Istio:** Feature-rich, observability, mTLS107- **Linkerd:** Simpler, Rust-based, lightweight108- **Consul Connect:** HashiCorp ecosystem109- **When:** Complex inter-service communication, need per-request metrics110111### Message Queues112- **RabbitMQ:** Classic, reliable, AMQP113- **Apache Kafka:** High-throughput streaming114- **AWS SQS:** Managed, serverless115- **BullMQ:** Redis-based, Node.js116117### Observability (OpenTelemetry)118- **Tracing:** Jaeger, Tempo, Honeycomb119- **Metrics:** Prometheus, Datadog120- **Logs:** Grafana Loki, CloudWatch121122## 📊 OpenTelemetry Integration123124### Distributed Tracing125```javascript126const { trace } = require('@opentelemetry/api');127128async function callExternalAPI(userId) {129 const span = trace.getActiveSpan();130 span?.setAttribute('user.id', userId);131 span?.addEvent('calling_external_api');132 133 try {134 const response = await fetch('https://api.example.com/data');135 span?.setAttribute('http.status_code', response.status);136 return response.json();137 } catch (error) {138 span?.recordException(error);139 span?.setStatus({ code: SpanStatusCode.ERROR });140 throw error;141 }142}143```144145### Propagating Context146- **Trace ID:** Flows through all services147- **Baggage:** Carry metadata (user_id, tenant_id, feature_flags)148- **W3C Standard:** traceparent header149150## 🔒 Security Patterns151152### API Key Rotation153```1541. Generate new key1552. Deploy with both keys accepted (grace period)1563. Update clients to new key1574. Remove old key after 30 days158```159160### Webhook Signature Verification161```javascript162const crypto = require('crypto');163164function verifyWebhook(payload, signature, secret) {165 const hmac = crypto.createHmac('sha256', secret);166 const digest = hmac.update(payload).digest('hex');167 return crypto.timingSafeEqual(168 Buffer.from(signature),169 Buffer.from(digest)170 );171}172```173174### Rate Limiting Strategies175- **Fixed Window:** 100 req/hour (simple, can burst)176- **Sliding Window:** Smooths out bursts177- **Token Bucket:** Allows bursts up to capacity178- **Leaky Bucket:** Constant rate, smoothest