# API Integration Best Practices

Modern applications rely on dozens of third-party services: payment processors, CRMs, email providers, analytics platforms. **Integrating them well** separates reliable systems from fragile ones.

## Common API Integration Challenges

- **Rate limiting** (getting blocked for too many requests)
- **Authentication failures** (expired tokens, rotating keys)
- **Data inconsistency** (APIs changing without notice)
- **Performance bottlenecks** (slow downstream services)
- **Security risks** (exposed credentials, data leaks)

## Best Practices

### 1. Centralize Integration Logic

Don't scatter API calls throughout your codebase. Create a dedicated **integration layer**:

```typescript
// /src/integrations/stripe.ts
class StripeIntegration {
  private client: Stripe;

  constructor(apiKey: string) {
    this.client = new Stripe(apiKey);
  }

  async createPayment(amount: number, customerId: string) {
    // Centralized error handling
    // Retry logic
    // Logging
  }
}
```

**Benefits:**

- Single place to update API logic
- Consistent error handling
- Easier testing and mocking

### 2. Handle Rate Limiting Gracefully

Most APIs have rate limits. Exceed them, and requests fail. Implement **exponential backoff**:

```typescript
async function fetchWithRetry(url: string, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      if (response.status === 429) {
        // Too Many Requests - wait and retry
        const waitTime = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise((resolve) => setTimeout(resolve, waitTime));
        continue;
      }
      return response;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}
```

### 3. Implement Request Queuing

For high-volume APIs (email, SMS, notifications), use a **job queue**:

- **Redis Queue** (Node.js)
- **Sidekiq** (Ruby)
- **Celery** (Python)

**Benefits:**

- Process requests asynchronously
- Retry failed jobs automatically
- Scale workers independently

### 4. Secure Your Credentials

**Never hardcode API keys** in your codebase:

```bash
# ✅ Good - Environment variables
STRIPE_API_KEY=sk_live_xxxxx
DATABASE_URL=postgresql://...

# ❌ Bad - In code
const apiKey = "sk_live_xxxxx";
```

**Additional security:**

- Rotate keys quarterly
- Use different keys for dev/staging/prod
- Implement **IP whitelisting** where possible
- Use **secrets management** (AWS Secrets Manager, HashiCorp Vault)

### 5. Monitor and Alert

You can't fix what you don't measure. Track:

- **Success/error rates** per integration
- **Response times** (slow APIs indicate issues)
- **Rate limit usage** (approaching limits?)
- **Cost** (APIs can get expensive fast)

Tools: Datadog, New Relic, or custom dashboards.

### 6. Version Your Integrations

APIs change. Prepare for it:

```typescript
// v1/stripe.ts - Original implementation
// v2/stripe.ts - Updated for new API version
// index.ts - Routes to correct version
```

When an API provider announces deprecation:

1. **Create new integration** alongside old
2. **Migrate gradually** (canary releases)
3. **Monitor for differences**
4. **Retire old version** once stable

### 7. Design for Failure

APIs go down. Your app shouldn't crash with them:

```typescript
async function getCustomerData(customerId: string) {
  try {
    return await crmApi.getCustomer(customerId);
  } catch (error) {
    // Fallback to cache
    const cached = await cache.get(`customer:${customerId}`);
    if (cached) return cached;

    // Graceful degradation
    logger.error("CRM API down", { error, customerId });
    return { name: "Unknown", email: null }; // Partial data
  }
}
```

### 8. Validate Incoming Data

Never trust third-party APIs. Validate schemas:

```typescript
import { z } from "zod";

const CustomerSchema = z.object({
  id: z.string(),
  email: z.string().email(),
  name: z.string().min(1),
});

function validateCustomer(data: unknown) {
  return CustomerSchema.parse(data);
}
```

**Benefits:**

- Catch breaking changes early
- Prevent runtime errors
- Self-documenting code

### 9. Implement Idempotency

Network failures cause **duplicate requests**. Design your integrations to handle them:

```typescript
// Use idempotency keys
await stripe.paymentIntents.create({
  amount: 2000,
  currency: "usd",
  idempotency_key: `payment_${orderId}`, // Same key = same result
});
```

### 10. Document Your Integrations

Maintain an **integration catalog**:

| Integration | Purpose  | Owner       | Last Updated | Status         |
| ----------- | -------- | ----------- | ------------ | -------------- |
| Stripe      | Payments | Engineering | 2026-01-08   | ✅ Operational |
| SendGrid    | Email    | Marketing   | 2026-01-05   | ⚠️ Degraded    |

## Common Integration Patterns

### 1. Sync vs. Async

| Pattern             | Use Case                              | Example                  |
| ------------------- | ------------------------------------- | ------------------------ |
| **Sync (blocking)** | User-facing, needs immediate response | Payment processing       |
| **Async (queue)**   | Background tasks, non-urgent          | Email sending, analytics |

### 2. Webhook Handling

For APIs that push data to you (webhooks):

```typescript
// Verify webhook signature
function verifyWebhook(signature: string, payload: string) {
  const hmac = crypto.createHmac("sha256", WEBHOOK_SECRET);
  const digest = hmac.update(payload).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}

// Process idempotently
async function handleWebhook(event: WebhookEvent) {
  const exists = await processedEvent.findUnique({
    where: { id: event.id },
  });
  if (exists) return; // Already processed

  // Process event
  await processEvent(event);
  await processedEvent.create({ data: { id: event.id } });
}
```

### 3. API Gateway Pattern

For multiple third-party integrations, use a **gateway**:

```
Your App → API Gateway → [Stripe, Salesforce, HubSpot, ...]
```

**Benefits:**

- Single authentication point
- Centralized rate limiting
- Unified logging/monitoring

## Common Pitfalls

| Pitfall                          | Why It Happens                         | Fix                                             |
| -------------------------------- | -------------------------------------- | ----------------------------------------------- |
| **Hardcoded API keys**           | Quick development, forgot security     | Use environment variables, secrets management   |
| **No retry logic**               | Assumes perfect uptime                 | Implement exponential backoff with max retries  |
| **Scattered API calls**          | Copy-paste implementation across files | Centralize integration logic in dedicated layer |
| **Ignoring rate limits**         | Didn't read API docs thoroughly        | Track usage, implement request queuing          |
| **Not validating responses**     | Trusting third-party data              | Schema validation with Zod or similar           |
| **Missing webhook verification** | Assumed secure endpoint                | Always verify signature with HMAC               |
| **No idempotency keys**          | Network failures cause retries         | Use idempotency keys for duplicate prevention   |
| **Silent API failures**          | Poor error handling                    | Implement comprehensive monitoring/alerting     |

## Tools We Recommend

| Purpose       | Tool                             |
| ------------- | -------------------------------- |
| HTTP Client   | Axios, Fetch (Node.js)           |
| Job Queues    | BullMQ (Node.js), Sidekiq (Ruby) |
| Monitoring    | Datadog, Sentry                  |
| Documentation | Stoplight, Swagger               |
| Testing       | MSW (Mock Service Worker)        |

---

**Need help integrating APIs into your system?** We've built dozens of integrations across payment, CRM, email, and logistics providers. [Let's talk](https://innovativeprospects.com/contact).