Integrations

Connecting to third-party APIs, webhooks, message queues, and external services with reliability and security.

Integrations

Connecting to third-party APIs, webhooks, message queues, and external services with reliability and security.

Modern applications rarely exist in isolation — they integrate with payment processors, email providers, analytics tools, and business platforms. We build reliable, secure, and maintainable integrations that scale.

Integration philosophy

We treat third-party APIs as unreliable — they go down, rate-limit requests, and change without notice. Our integrations are built with retries, circuit breakers, and graceful degradation from day one.

Types of Integrations

1. REST API Integrations

Calling external HTTP APIs (payment, shipping, email, etc.).

Key practices:

  • Timeout handling: Set timeouts (e.g., 10s) to avoid hanging requests
  • Retry logic: Exponential backoff for transient failures (503, 429)
  • Error handling: Parse error responses, log failures
  • Rate limiting: Respect API rate limits (check headers: X-RateLimit-Remaining)
// Example: Stripe Payment Integration
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2023-10-16',
  timeout: 10000, // 10 seconds
  maxNetworkRetries: 3, // Retry failed requests
});

async function createPaymentIntent(amount: number, currency: string) {
  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency,
      automatic_payment_methods: { enabled: true },
    });
    return paymentIntent;
  } catch (error) {
    if (error.type === 'StripeCardError') {
      // Card was declined
      throw new BadRequestException(error.message);
    }
    // Log and rethrow
    logger.error('Stripe payment failed', error);
    throw new ServiceUnavailableException('Payment service unavailable');
  }
}

2. Webhooks (Receiving Events)

External services send HTTP callbacks when events occur (e.g., Stripe payment succeeded, GitHub PR merged).

Key practices:

  • Signature verification: Verify requests are from the claimed sender (HMAC)
  • Idempotency: Process duplicate webhooks safely (store event IDs)
  • Async processing: Queue webhooks for background processing (don't block the response)
  • Retry handling: Return 200 OK to acknowledge receipt; retry logic is on sender's side
// Example: GitHub Webhook Handler
@Post('webhooks/github')
async handleGitHubWebhook(
  @Body() payload: any,
  @Headers('x-hub-signature-256') signature: string,
) {
  // 1. Verify signature
  const isValid = this.verifyGitHubSignature(payload, signature);
  if (!isValid) {
    throw new UnauthorizedException('Invalid signature');
  }

  // 2. Check for duplicate (idempotency)
  const eventId = payload.headers['x-github-delivery'];
  const alreadyProcessed = await this.redis.get(`webhook:${eventId}`);
  if (alreadyProcessed) {
    return { status: 'already_processed' };
  }

  // 3. Queue for async processing
  await this.queue.add('github-webhook', payload);
  await this.redis.setex(`webhook:${eventId}`, 86400, 'true'); // 24h TTL

  // 4. Respond immediately
  return { status: 'received' };
}

private verifyGitHubSignature(payload: any, signature: string): boolean {
  const secret = process.env.GITHUB_WEBHOOK_SECRET;
  const hmac = crypto.createHmac('sha256', secret);
  const expectedSignature = 'sha256=' + hmac.update(JSON.stringify(payload)).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}

3. Message Queues

Asynchronous communication between services or background job processing.

Use cases:

  • Email sending: Queue emails instead of blocking requests
  • Image processing: Resize/optimize images in background
  • Data sync: Sync data between systems asynchronously

Technologies:

  • RabbitMQ: Feature-rich, supports complex routing
  • AWS SQS: Managed, scales automatically
  • Redis Pub/Sub: Lightweight, for simple use cases
// Example: BullMQ (Redis-based queue)
import { Queue, Worker } from 'bullmq';

const emailQueue = new Queue('email', {
  connection: { host: 'localhost', port: 6379 },
});

// Producer: Add jobs to queue
async function sendWelcomeEmail(userId: string) {
  await emailQueue.add('welcome-email', { userId });
}

// Consumer: Process jobs
const worker = new Worker(
  'email',
  async (job) => {
    const { userId } = job.data;
    const user = await getUserById(userId);
    await emailService.send({
      to: user.email,
      subject: 'Welcome!',
      body: renderWelcomeEmail(user),
    });
  },
  { connection: { host: 'localhost', port: 6379 } },
);

Queue pitfalls

  • Poison messages: Jobs that always fail can block the queue → use dead letter queues
  • No retries: Configure max retries and backoff strategies
  • Lost jobs: Use persistent queues (Redis AOF, RabbitMQ durability)

4. OAuth & SSO Integrations

Letting users sign in with Google, GitHub, Microsoft, etc.

Key practices:

  • OAuth 2.0 flow: Authorization code flow for web apps, PKCE for mobile
  • Token storage: Store access/refresh tokens securely (encrypted in DB)
  • Scope management: Request minimal scopes (e.g., email, profile)
// Example: Google OAuth with Passport.js
passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: '/auth/google/callback',
    },
    async (accessToken, refreshToken, profile, done) => {
      // Find or create user
      let user = await User.findOne({ googleId: profile.id });
      if (!user) {
        user = await User.create({
          googleId: profile.id,
          email: profile.emails[0].value,
          name: profile.displayName,
        });
      }
      return done(null, user);
    }
  )
);

Common Integration Patterns

Circuit Breaker

Stop calling failing services to prevent cascading failures.

If 5 requests fail → open circuit → return fallback response → retry after 30s

Exponential Backoff

Retry failed requests with increasing delays.

1st retry: 1s → 2nd: 2s → 3rd: 4s → 4th: 8s (max 5 retries)

API Key Rotation

Rotate API keys/secrets without downtime.

  1. Generate new key → 2. Deploy with both keys → 3. Switch to new → 4. Remove old
CategoryServices
PaymentsStripe, PayPal, Square, Adyen
EmailSendGrid, Mailgun, AWS SES, Resend
SMSTwilio, Vonage, AWS SNS
Push NotificationsFirebase Cloud Messaging, OneSignal, AWS SNS
AnalyticsSegment, Mixpanel, Amplitude, Google Analytics
StorageAWS S3, Cloudflare R2, Google Cloud Storage
AuthAuth0, Firebase Auth, Supabase Auth, Clerk
MonitoringSentry, Datadog, New Relic, LogRocket
Business ToolsSlack, Salesforce, HubSpot, Zendesk

Integration testing

Use test/sandbox modes for third-party services. Mock external APIs in unit tests, but run integration tests against real sandbox environments.

Security Best Practices

Never Hardcode Secrets

Store API keys in environment variables or secret managers (AWS Secrets Manager, HashiCorp Vault).

Verify Webhook Signatures

Always verify HMAC signatures to prevent spoofed webhook requests.

Use HTTPS Everywhere

All external API calls and webhooks must use HTTPS (TLS 1.2+).

Log (But Redact Secrets)

Log API requests/responses for debugging, but redact tokens, passwords, and PII.

Explore More