Production Engineering/
Lesson

Every serious production app relies on services it didn't build. Instead of writing your own payment processor, you integrate Stripe. Instead of building email infrastructure, you use Resend. This lesson covers the patterns that connect your app to those external services, patterns you'll use constantly in your career.

Why use third-party services

Building payments, authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token., or email delivery from scratch is a trap. Each one looks simple on the surface and hides enormous complexity underneath, PCI compliance, deliverability, security audits, edge cases. Third-party services solve that complexity so you can focus on what your app actually does.

Think of it like plumbing. You don't install your own water treatment plant when you build a house, you connect to the municipal system. Third-party APIs are the municipal systems of the software world.

Service categoryPopular optionsWhat it saves you
PaymentsStripe, Paddle, BraintreePCI compliance, fraud detection, chargeback handling
AuthenticationClerk, Auth0, Firebase AuthPassword hashing, 2FA, session management, SSO
EmailResend, SendGrid, PostmarkDeliverability, DNS setup, bounce handling
StorageCloudflare R2, AWS S3Infrastructure, redundancy, CDN
MonitoringSentry, DatadogError tracking, alerting, dashboards
02

AuthenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. methods

APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. keys

The simplest and most common approach. You get a secret string from the providerWhat is provider?A wrapper component that makes data available to all components nested inside it without passing props manually.'s dashboard, and you include it with every request. Most SDKs handle this automatically once you initialize with your key.

// The SDK handles auth headers for you
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

// Under the hood it's just adding an Authorization header
// Authorization: Bearer sk_live_...
Never hardcode API keys in your source code. Use environment variables (.env locally, secrets manager in production). A leaked key can cost you thousands of dollars in fraudulent API calls before you notice.

OAuth 2.0What is oauth?An authorization protocol that lets users grant a third-party app limited access to their account on another service without sharing their password.

OAuth is used when your app needs to act on behalf of a user, for example, posting to their Twitter account or reading their Google Calendar. The flow has three steps: redirect the user to the provider, they authorize your app, the provider gives you a tokenWhat is token?The smallest unit of text an LLM processes - roughly three-quarters of a word. API pricing is based on how many tokens you use..

// Step 1: Redirect user to the provider's authorization page
const authUrl = `https://accounts.google.com/o/oauth2/auth?
  client_id=${CLIENT_ID}
  &redirect_uri=${REDIRECT_URI}
  &scope=email profile
  &response_type=code`;

// Step 2: Provider redirects back with a code
// GET /callback?code=abc123

// Step 3: Exchange the code for an access token
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  body: JSON.stringify({
    code: req.query.code,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    redirect_uri: REDIRECT_URI,
    grant_type: 'authorization_code'
  })
});

const { access_token } = await tokenResponse.json();

Webhooks

Webhooks are the inverse of a regular API call. Instead of you asking "did anything happen?", the service pushes an HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. POST to your server the moment something happens. Think of it like signing up for text alerts instead of refreshing a web page repeatedly.

// Your endpoint that Stripe (or any service) will POST to
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
  const event = JSON.parse(req.body);

  switch (event.type) {
    case 'payment_intent.succeeded':
      console.log('Payment succeeded:', event.data.object.id);
      // Provision the user's subscription
      break;
    case 'customer.subscription.deleted':
      console.log('Subscription cancelled');
      // Revoke access
      break;
  }

  // Always respond quickly - the service will retry if you don't
  res.json({ received: true });
});
Webhooks must be verified. Without verification, anyone can POST fake events to your endpoint. Most providers give you a signing secret and a library function to verify the signature.
03

SDKs vs raw RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. calls

Most major services offer an official SDKWhat is sdk?A pre-built library from a service provider that wraps their API into convenient functions you call in your code instead of writing raw HTTP requests. (a library you install via npm). You can also always call their REST APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. directly with fetch. Both work, and the right choice depends on your situation.

ApproachProsCons
SDKLess boilerplate, handles auth and serialization, TypeScript types includedAdds a dependency, may lag behind the API
Raw RESTNo dependency, full control, works anywhereMore code, you handle auth headers and error parsing manually

For most projects, start with the SDK. Switch to raw REST only if the SDK is missing a feature you need or you want zero dependencies.

04

Rate limitingWhat is rate limiting?Restricting how many requests a client can make within a time window. Prevents brute-force attacks and protects your API from being overwhelmed.

Every APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. providerWhat is provider?A wrapper component that makes data available to all components nested inside it without passing props manually. caps how many requests you can make per second or per day. Exceed the limit and you get a 429 Too Many Requests response. Your code must handle this, a crash loop against a rate-limited API is a bad day.

async function callWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const isRateLimit = error.status === 429 || error.code === 'rate_limit';

      if (isRateLimit && attempt < maxRetries - 1) {
        // Exponential backoff: wait longer each time
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      throw error; // Give up after max retries
    }
  }
}

// Usage
const result = await callWithRetry(() => stripe.customers.list({ limit: 100 }));
05

Quick reference

Auth typeWhen to useExample
API keyServer-to-server callsStripe, Resend, OpenAI
OAuth 2.0Acting on behalf of a userGoogle, GitHub, Twitter integrations
WebhookReceiving real-time eventsPayment confirmations, email bounces
JWTStateless session verificationClerk, Auth0 tokens
javascript
// Secure API call pattern
class APIClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseURL = 'https://api.service.com';
  }

  async request(endpoint, options = {}) {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      ...options,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    if (!response.ok) {
      throw new Error(`API Error: ${response.status}`);
    }

    return response.json();
  }
}