Pattern 32: System Integration
Intent
Safely connect systems to external services, APIs, and data sources through protected boundaries, rigorous input validation, authentication verification, rate limiting, and circuit breakers, treating all external data as potentially harmful until proven safe, just as welders wear goggles to protect against sparks.
Also Known As
- API Integration Patterns
- External Service Integration
- Safe Data Exchange
- Protected Boundaries
- Integration Security
Problem
Systems must integrate with external services, but external data can be malicious, corrupted, or compromise security. Integration points are attack vectors that must be protected like welding requires eye protection.
The "welding without goggles" problem:
Richard's analogy:
Welding steel without goggles:
- Sparks fly toward eyes
- UV radiation damages retina
- Metal fragments can blind
- PROTECTION REQUIRED! 👓
System integration without protection:
- Malicious data flows in
- SQL injection damages database
- XSS attacks compromise users
- PROTECTION REQUIRED! 🛡️
Both scenarios: Damage is INEVITABLE without protection!
The unprotected API disaster:
Developer builds API without protection:
// DANGEROUS: No validation, no authentication!
app.post('/api/family/create', async (req, res) => {
// Trust external data blindly
await db.query(`
INSERT INTO families (name, email)
VALUES ('${req.body.name}', '${req.body.email}')
`);
res.json({success: true});
});
Attacker sends:
POST /api/family/create
{
"name": "Robert'); DROP TABLE families; --",
"email": "attacker@evil.com"
}
Result:
INSERT INTO families (name, email)
VALUES ('Robert'); DROP TABLE families; --', 'attacker@evil.com')
-- SQL INJECTION! Families table DELETED! 😱💀
All family data DESTROYED! No backups (oops)! Business DEAD!
The "trusting external data" problem:
Integrate with third-party payment API:
// DANGEROUS: Trust webhook data
app.post('/webhook/payment', async (req, res) => {
// Attacker can POST to this endpoint!
const paymentData = req.body;
await db.query(`
UPDATE families
SET payment_status = 'paid',
balance = 0
WHERE family_id = ?
`, [paymentData.family_id]);
res.json({success: true});
});
Attacker discovers webhook URL, sends:
POST /webhook/payment
{
"family_id": "attacker_family_123",
"amount": 0,
"status": "paid"
}
Result: Attacker marks themselves as paid without paying! Free service! 😱
The third-party compromise:
System integrates with email service:
await emailService.send({
to: family.email,
subject: 'Welcome!',
body: emailTemplate
});
Email service gets hacked. Attacker modifies API to: - Steal all email addresses (data exfiltration) - Inject malicious links in emails (phishing) - Use your domain for spam (reputation damage)
Your system compromised through THIRD PARTY! 😱
The cascading failure:
Payment service goes down:
app.post('/enroll-family', async (req, res) => {
await createFamily(req.body); // ✅ Works
await processPayment(req.body.payment); // ❌ Hangs (service down!)
// Request never completes! Times out after 30 seconds!
// Every enrollment request locks up!
// System UNUSABLE! 😱
});
One external service failure brings down ENTIRE system!
The data injection attack:
Import families from CSV:
app.post('/import-csv', async (req, res) => {
const csv = req.body.csv_data;
const rows = csv.split('\n');
for (const row of rows) {
const [name, email] = row.split(',');
// DANGEROUS: Inject directly into database!
await db.query(`
INSERT INTO families (name, email) VALUES (?, ?)
`, [name, email]);
}
});
Attacker uploads CSV:
Normal Family,normal@example.com
Evil Family,evil@example.com
<script>alert('XSS')</script>,xss@evil.com
'; DROP TABLE families; --,sql@evil.com
Result: - XSS injected into database (later executed in browser) - SQL injection attempt (blocked by parameterized query, but close call!) - Malicious data in system!
External data = SPARKS that can DAMAGE system! 🔥
The rate limit attack:
API has no rate limiting:
app.get('/api/families', async (req, res) => {
// No protection!
const families = await db.query('SELECT * FROM families');
res.json(families);
});
Attacker sends 100,000 requests per second: - Database overwhelmed (100,000 queries/second) - Server CPU maxed out - Legitimate users can't access - Denial of Service (DoS) attack succeeds! 😱
What we need: Protected Integration
Welding goggles protect eyes from sparks: - Filter harmful UV radiation - Block flying metal fragments - Allow vision (let good light through) - MANDATORY for safety!
Integration protection guards system from malicious data: - Validate/sanitize all input (filter SQL injection, XSS) - Authenticate all requests (block unauthorized) - Rate limit requests (prevent DoS) - Circuit breakers (isolate failures) - MANDATORY for security!
Integration protection layers:
1. Authentication/Authorization: - Verify caller identity (API keys, OAuth) - Check permissions (authorized?) - Reject unauthorized (block attacker)
2. Input Validation: - Whitelist acceptable formats - Sanitize dangerous characters - Reject malformed data
3. Rate Limiting: - Max requests per minute/hour - Prevent DoS attacks - Protect system resources
4. Circuit Breakers: - Detect external service failures - Stop calling failed services - Prevent cascading failures
5. Data Transformation Boundaries: - Sanitize external data - Transform to internal format - Isolate external structure
6. Webhook Verification: - Verify signatures (HMAC) - Ensure authenticity - Prevent spoofing
Without integration protection: - SQL injection (data destroyed) - XSS attacks (users compromised) - DoS attacks (system unusable) - Third-party compromise (security breach) - Cascading failures (total outage)
With integration protection: - SQL injection blocked (parameterized queries) - XSS sanitized (HTML encoding) - DoS prevented (rate limiting) - Third-party isolated (circuit breakers) - Failures contained (graceful degradation)
Context
When this pattern applies:
- Integrating with external APIs
- Accepting webhooks from third parties
- Importing external data
- Exposing APIs to partners
- Connecting to payment processors
- Any external data flowing into system
When this pattern may not be needed:
- Completely internal systems (no external data)
- Single-user desktop apps (no network)
- Static websites (no dynamic data)
Forces
Competing concerns:
1. Openness vs Security - Open API = easy integration, vulnerable - Locked down = secure, hard to integrate - Balance: Secure by default, documented access
2. Trust vs Verification - Trust third parties = simple, risky - Verify everything = complex, secure - Balance: "Trust but verify" (verify!)
3. Performance vs Validation - Skip validation = fast, dangerous - Heavy validation = slow, safe - Balance: Efficient validation (whitelist)
4. Availability vs Protection - Accept all requests = available, DoS risk - Rate limit = protected, might block legitimate - Balance: Generous limits, monitor abuse
5. Convenience vs Safety - No auth = convenient, insecure - Strong auth = safe, friction - Balance: API keys (simple + secure)
Solution
Build protected integration with:
1. API Authentication & Authorization
API key authentication:
const crypto = require('crypto');
// Middleware: Verify API key
async function requireAPIKey(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({error: 'API key required'});
}
// Verify API key (hash comparison, constant-time to prevent timing attacks)
const validKey = await db.query(
'SELECT * FROM api_keys WHERE key_hash = ? AND active = 1',
[crypto.createHash('sha256').update(apiKey).digest('hex')]
);
if (!validKey.length) {
// Log failed attempt
await auditLog.record({
action: 'INVALID_API_KEY',
ip: req.ip,
attempted_key: apiKey.substring(0, 10) + '...',
timestamp: new Date()
});
return res.status(401).json({error: 'Invalid API key'});
}
// Attach API key info to request
req.apiKey = validKey[0];
// Log successful API access
await auditLog.record({
action: 'API_ACCESS',
api_key_id: validKey[0].id,
endpoint: req.path,
ip: req.ip
});
next();
}
// Protected endpoint
app.get('/api/families', requireAPIKey, async (req, res) => {
// Only reaches here if API key valid!
const families = await db.query('SELECT * FROM families');
res.json(families);
});
2. Input Validation (Welding Goggles!)
Validate and sanitize ALL external input:
const validator = require('validator');
const xss = require('xss');
// Validation schema
const familySchema = {
name: {
required: true,
type: 'string',
minLength: 2,
maxLength: 200,
pattern: /^[a-zA-Z\s\-']+$/ // Only letters, spaces, hyphens, apostrophes
},
email: {
required: true,
type: 'email',
validate: validator.isEmail
},
phone: {
required: false,
type: 'string',
pattern: /^\+?[\d\s\-\(\)]+$/ // Phone number format
},
children_count: {
required: true,
type: 'integer',
min: 1,
max: 20
}
};
function validateInput(data, schema) {
const errors = [];
const sanitized = {};
for (const [field, rules] of Object.entries(schema)) {
const value = data[field];
// Required check
if (rules.required && !value) {
errors.push(`${field} is required`);
continue;
}
if (!value) continue; // Optional field, skip
// Type check
if (rules.type === 'string' && typeof value !== 'string') {
errors.push(`${field} must be a string`);
continue;
}
if (rules.type === 'integer' && !Number.isInteger(value)) {
errors.push(`${field} must be an integer`);
continue;
}
// Length check
if (rules.minLength && value.length < rules.minLength) {
errors.push(`${field} must be at least ${rules.minLength} characters`);
continue;
}
if (rules.maxLength && value.length > rules.maxLength) {
errors.push(`${field} must be at most ${rules.maxLength} characters`);
continue;
}
// Pattern check
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`${field} has invalid format`);
continue;
}
// Custom validation
if (rules.validate && !rules.validate(value)) {
errors.push(`${field} is invalid`);
continue;
}
// Range check (for numbers)
if (rules.type === 'integer') {
if (rules.min !== undefined && value < rules.min) {
errors.push(`${field} must be at least ${rules.min}`);
continue;
}
if (rules.max !== undefined && value > rules.max) {
errors.push(`${field} must be at most ${rules.max}`);
continue;
}
}
// Sanitize (remove XSS)
if (rules.type === 'string') {
sanitized[field] = xss(value);
} else {
sanitized[field] = value;
}
}
if (errors.length > 0) {
throw new Error(errors.join(', '));
}
return sanitized;
}
// Use validation
app.post('/api/family/create', requireAPIKey, async (req, res) => {
try {
// VALIDATE before using!
const sanitizedData = validateInput(req.body, familySchema);
// Safe to use (validated, sanitized)
await db.query(`
INSERT INTO families (name, email, phone, children_count)
VALUES (?, ?, ?, ?)
`, [
sanitizedData.name,
sanitizedData.email,
sanitizedData.phone,
sanitizedData.children_count
]);
res.json({success: true});
} catch (error) {
// Validation failed
await auditLog.record({
action: 'VALIDATION_FAILED',
error: error.message,
data: req.body,
ip: req.ip
});
res.status(400).json({error: error.message});
}
});
3. Rate Limiting (Prevent DoS)
Limit requests per IP/API key:
const rateLimit = require('express-rate-limit');
// Create rate limiter
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Max 100 requests per window
message: 'Too many requests, please try again later',
// Key by API key (or IP if no API key)
keyGenerator: (req) => {
return req.apiKey?.id || req.ip;
},
// Log rate limit hits
handler: async (req, res) => {
await auditLog.record({
action: 'RATE_LIMIT_EXCEEDED',
api_key_id: req.apiKey?.id,
ip: req.ip,
endpoint: req.path
});
res.status(429).json({
error: 'Too many requests',
retryAfter: Math.ceil(req.rateLimit.resetTime / 1000)
});
}
});
// Apply rate limiting
app.use('/api/', apiLimiter);
4. Circuit Breaker (Isolate Failures)
Protect from cascading failures:
class CircuitBreaker {
constructor(service, options = {}) {
this.service = service;
this.failureThreshold = options.failureThreshold || 5;
this.successThreshold = options.successThreshold || 2;
this.timeout = options.timeout || 5000;
this.resetTimeout = options.resetTimeout || 60000;
this.state = 'CLOSED'; // CLOSED = normal, OPEN = failing, HALF_OPEN = testing
this.failures = 0;
this.successes = 0;
this.nextAttempt = Date.now();
}
async call(...args) {
// OPEN: Circuit broken, fail fast
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error(`Circuit breaker OPEN - ${this.service.name} unavailable`);
}
// Try recovery
this.state = 'HALF_OPEN';
this.successes = 0;
}
try {
// Call service with timeout
const result = await Promise.race([
this.service(...args),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), this.timeout)
)
]);
// Success!
this.onSuccess();
return result;
} catch (error) {
// Failure!
this.onFailure();
throw error;
}
}
onSuccess() {
this.failures = 0;
if (this.state === 'HALF_OPEN') {
this.successes++;
if (this.successes >= this.successThreshold) {
// Recovered! Close circuit
this.state = 'CLOSED';
console.log(`Circuit breaker CLOSED - ${this.service.name} recovered`);
}
}
}
onFailure() {
this.failures++;
this.successes = 0;
if (this.failures >= this.failureThreshold) {
// Too many failures! Open circuit
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.resetTimeout;
console.log(`Circuit breaker OPEN - ${this.service.name} marked unavailable`);
// Alert monitoring
alerting.notify({
severity: 'HIGH',
message: `Circuit breaker OPEN for ${this.service.name}`,
failures: this.failures
});
}
}
}
// Wrap external service calls
async function processPayment(familyId, amount) {
return await stripe.charges.create({
amount: amount * 100,
currency: 'usd',
customer: familyId
});
}
const paymentCircuit = new CircuitBreaker(processPayment, {
failureThreshold: 5,
timeout: 5000
});
// Use circuit breaker
app.post('/api/payment', async (req, res) => {
try {
const result = await paymentCircuit.call(req.body.family_id, req.body.amount);
res.json({success: true, charge_id: result.id});
} catch (error) {
// Graceful degradation
console.log('Payment processing unavailable, queueing...');
await queue.enqueue('process-payment', {
family_id: req.body.family_id,
amount: req.body.amount
});
res.json({
success: true,
message: 'Payment queued for processing',
status: 'pending'
});
}
});
5. Webhook Verification (Prevent Spoofing)
Verify webhook authenticity:
const crypto = require('crypto');
// Verify Stripe webhook signature
function verifyWebhookSignature(req, res, next) {
const signature = req.headers['stripe-signature'];
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
try {
// Verify signature using HMAC
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(req.rawBody) // Need raw body, not parsed JSON!
.digest('hex');
const providedSignature = signature.split(',')
.find(s => s.startsWith('v1='))
.substring(3);
// Constant-time comparison (prevent timing attacks)
if (!crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(providedSignature)
)) {
throw new Error('Invalid signature');
}
// Signature valid!
next();
} catch (error) {
// Log failed verification
await auditLog.record({
action: 'WEBHOOK_VERIFICATION_FAILED',
ip: req.ip,
signature: signature,
error: error.message
});
return res.status(401).json({error: 'Invalid signature'});
}
}
// Webhook endpoint (protected!)
app.post('/webhook/stripe',
express.raw({type: 'application/json'}), // Need raw body for signature
verifyWebhookSignature,
async (req, res) => {
// Signature verified - webhook is authentic!
const event = JSON.parse(req.body);
if (event.type === 'charge.succeeded') {
await handlePaymentSuccess(event.data);
}
res.json({received: true});
}
);
6. Data Transformation Boundaries
Transform external data to internal format:
// External API returns different format
class ExternalAPIAdapter {
async getFamily(externalId) {
// Call external API
const externalData = await externalAPI.getFamily(externalId);
// External format:
// {
// familyName: "...",
// contactEmail: "...",
// kids: [{...}]
// }
// Transform to internal format (isolate external structure!)
return {
family_id: externalData.id,
name: this.sanitize(externalData.familyName),
email: validator.isEmail(externalData.contactEmail)
? externalData.contactEmail
: null,
children_count: Array.isArray(externalData.kids)
? externalData.kids.length
: 0,
source: 'external_api',
imported_at: new Date()
};
}
sanitize(value) {
if (typeof value !== 'string') return '';
// Remove HTML, scripts, etc.
return xss(value);
}
}
// Use adapter (internal code never sees external format)
const adapter = new ExternalAPIAdapter();
const family = await adapter.getFamily(externalId);
// Safe to use (validated, sanitized, internal format)
await db.query(`
INSERT INTO families (family_id, name, email, children_count)
VALUES (?, ?, ?, ?)
`, [family.family_id, family.name, family.email, family.children_count]);
Structure
Integration Architecture
┌──────────────────┐
│ External World │
│ (Untrusted Data) │
└──────────────────┘
│
┌───────▼────────┐
│ API Gateway │ ← Authentication
│ (First Line │ ← Rate Limiting
│ of Defense) │ ← DDoS Protection
└───────┬────────┘
│
┌───────────▼───────────┐
│ Input Validation │ ← Sanitize
│ (Welding Goggles!) │ ← Validate format
│ │ ← Whitelist
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Circuit Breakers │ ← Isolate failures
│ (Failure Isolation) │ ← Prevent cascade
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Data Transformation │ ← External → Internal
│ (Boundary Layer) │ ← Sanitize again
└───────────┬───────────┘
│
┌───────▼────────┐
│ Internal │
│ System │
│ (Protected) │
└────────────────┘
Implementation
[Code examples above demonstrate comprehensive implementation]
Variations
By Integration Type
REST APIs: - API keys for auth - Rate limiting - JSON validation
Webhooks: - HMAC signature verification - Replay attack prevention - Idempotency
File Imports: - Virus scanning - Format validation - Sandboxed processing
Database Integration: - Read-only connections - Query timeouts - Connection pooling
Consequences
Benefits
1. Attack prevention SQL injection, XSS, DoS blocked.
2. Failure isolation Third-party failures don't cascade.
3. Data integrity All external data validated/sanitized.
4. Audit trail All integration activity logged.
5. Graceful degradation System functions even when external services down.
Costs
1. Complexity More validation, more checks.
2. Performance overhead Validation takes CPU/time.
3. Development friction Can't quickly integrate without security review.
4. False positives Overly strict validation might reject valid data.
Sample Code
Complete protected integration:
[Code examples above demonstrate this]
Known Uses
Stripe - Webhook signature verification - API key authentication - Rate limiting - PCI compliance boundaries
GitHub - OAuth for API access - Webhook secret verification - Rate limiting (5000/hour) - Input sanitization
Twilio - Request signing - IP whitelisting - Rate limiting - Webhook validation
AWS - IAM authentication - Request signing (SigV4) - Rate limiting - Input validation
Related Patterns
Requires: - Pattern 31: Privacy Architecture - secure data handling - Pattern 30: Scalability - rate limiting infrastructure
Enables: - Safe third-party integration - Partner API access - Webhook processing - Data import/export
References
Academic Foundations
- Hohpe, Gregor, and Bobby Woolf (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. ISBN: 978-0321200686 - https://www.enterpriseintegrationpatterns.com/ - The definitive integration patterns reference
- Newman, Sam (2021). Building Microservices (2nd ed.). O'Reilly. ISBN: 978-1492034025 - Integration in microservices
- Nygard, Michael T. (2018). Release It! (2nd ed.). Pragmatic Bookshelf. ISBN: 978-1680502398 - Circuit breakers and resilience patterns
- Richardson, Chris (2018). Microservices Patterns. Manning. ISBN: 978-1617294549 - Service integration patterns
API Security
- OWASP API Security Top 10: https://owasp.org/www-project-api-security/ - Critical API security risks
- OWASP Input Validation: https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html - Validation best practices
- OAuth 2.0: https://oauth.net/2/ - Authorization framework (RFC 6749)
- OpenID Connect: https://openid.net/connect/ - Identity layer on OAuth 2.0
- JWT: https://jwt.io/ - JSON Web Tokens for secure API authentication
API Design
- REST API Design: Fielding, R.T. (2000). "Architectural Styles and the Design of Network-based Software Architectures." https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm - Original REST dissertation
- OpenAPI Specification: https://swagger.io/specification/ - API documentation standard
- GraphQL: https://graphql.org/ - Query language for APIs
- gRPC: https://grpc.io/ - High-performance RPC framework
- API Design Patterns: Lauret, A. (2021). The Design of Web APIs. Manning. ISBN: 978-1617295102
Resilience Patterns
- Circuit Breaker: Nygard, M. https://martinfowler.com/bliki/CircuitBreaker.html - Prevent cascading failures
- Retry with Exponential Backoff: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ - AWS best practices
- Bulkhead Pattern: https://docs.microsoft.com/en-us/azure/architecture/patterns/bulkhead - Isolate resources
- Timeout Patterns: https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/ - AWS Builders Library
Integration Platforms
- Apache Camel: https://camel.apache.org/ - Open source integration framework
- MuleSoft: https://www.mulesoft.com/ - Enterprise integration platform
- Dell Boomi: https://boomi.com/ - Cloud integration platform
- Zapier: https://zapier.com/ - No-code integration
- n8n: https://n8n.io/ - Open source workflow automation
Related Trilogy Patterns
- Pattern 23: Triggered Interventions - Event-driven integration
- Pattern 29: Real-Time Processing - Streaming integration
- Pattern 30: Scalability Patterns - Scale integrations
- Pattern 32: System Integration - Secure, reliable integration architecture
- Volume 3, Pattern 21: External Data Integration - Form-level integration
- Volume 3, Pattern 25: Cross-System Workflows - Multi-system orchestration
Practical Implementation
- Resilience4j: https://resilience4j.readme.io/ - Fault tolerance library (Java)
- Polly: https://github.com/App-vNext/Polly - Resilience and transient-fault-handling (.NET)
- Hystrix: https://github.com/Netflix/Hystrix - Latency and fault tolerance (Netflix, maintenance mode)
- Kong: https://konghq.com/ - API gateway with plugins
- Apigee: https://cloud.google.com/apigee - Full-lifecycle API management
API Gateway & Management
- AWS API Gateway: https://aws.amazon.com/api-gateway/ - Managed API gateway
- Azure API Management: https://azure.microsoft.com/en-us/services/api-management/
- Google Cloud API Gateway: https://cloud.google.com/api-gateway
- Tyk: https://tyk.io/ - Open source API gateway
- KrakenD: https://www.krakend.io/ - Ultra-high performance API gateway
Monitoring & Observability
- OpenTelemetry: https://opentelemetry.io/ - Observability framework (traces, metrics, logs)
- Jaeger: https://www.jaegertracing.io/ - Distributed tracing
- Zipkin: https://zipkin.io/ - Distributed tracing system
- Datadog APM: https://www.datadoghq.com/product/apm/ - Application performance monitoring