Volume 2: Organizational Intelligence Platforms

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

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

API Design

Resilience Patterns

Integration Platforms

  • 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

API Gateway & Management

Monitoring & Observability