Integrations Basics
8 examples to get you started with third-party integrations for Node.js APIs - 6 basic and 2 intermediate.
Prerequisites
npm install stripe got
npm install -D typescript@5.6 tsxSee Stripe, Twilio & SDK Patterns for vendor-specific detail.
Basic Examples
1. Service Boundary
// Bad: route calls SDK
app.post("/checkout", async (req, res) => {
const stripe = new Stripe(process.env.STRIPE_KEY!);
const pi = await stripe.paymentIntents.create({ amount: 1000, currency: "usd" });
res.json(pi);
});
// Good: route calls integration service
app.post("/checkout", async (req, res) => {
const result = await paymentsService.createIntent(req.body);
res.status(201).json(result);
});- Swap Stripe mock in tests without HTTP layer
- One place for timeout, logging, and idempotency keys
Related: Use Cases & Services
2. Singleton SDK Client
// src/integrations/stripe-client.ts
import Stripe from "stripe";
let stripe: Stripe | null = null;
export function getStripe() {
if (!stripe) {
stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-11-20.acacia",
timeout: 10_000,
maxNetworkRetries: 0, // you own retry policy
});
}
return stripe;
}- Construct once per process
- Set explicit
timeout- SDK defaults may be too long for HTTP request budget - Disable SDK auto-retry if app layer handles backoff
3. Timeout Wrapper
export async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
const ac = new AbortController();
const timer = setTimeout(() => ac.abort(), ms);
try {
return await Promise.race([
promise,
new Promise<T>((_, reject) => {
ac.signal.addEventListener("abort", () => reject(new Error("timeout")));
}),
]);
} finally {
clearTimeout(timer);
}
}- Every outbound call gets a deadline
- See Timeouts Everywhere
4. Error Mapping
export function mapStripeError(err: unknown): AppError {
if (err instanceof Stripe.errors.StripeCardError) {
return new AppError("card_declined", 402, { code: err.code });
}
if (err instanceof Stripe.errors.StripeAPIError) {
return new AppError("payment_provider_unavailable", 503);
}
return new AppError("internal", 500);
}- Routes return consistent JSON error shape
- Log vendor request id, not full card numbers
5. Correlation in Logs
logger.info({
msg: "stripe_request",
requestId: req.headers["x-request-id"],
operation: "paymentIntents.create",
idempotencyKey: key,
});- Pass vendor idempotency key same as internal correlation where supported
- OpenTelemetry span per outbound SDK call
6. Sandbox vs Production Keys
const key = process.env.NODE_ENV === "production"
? process.env.STRIPE_LIVE_KEY!
: process.env.STRIPE_TEST_KEY!;- CI uses test keys only
- Separate webhook secrets per environment
- Never mix live keys in preview deploys
Intermediate Examples
7. Outbound HTTP Without SDK
import got from "got";
const client = got.extend({
prefixUrl: "https://api.partner.com/v1/",
timeout: { request: 8_000 },
headers: { Authorization: `Bearer ${process.env.PARTNER_TOKEN}` },
});
export async function fetchPartnerStatus(id: string) {
return client.get(`status/${id}`).json<PartnerStatus>();
}gotretries and hooks documented in Retry & Outbound Resilience
8. Webhook + API Together
Outbound: your API -> Stripe API (create payment)
Inbound: Stripe -> your webhook (payment succeeded)
- Both paths must be idempotent
- See Webhook Verification
FAQs
SDK or raw HTTP?
Official SDK when available (Stripe, Twilio, AWS). Raw HTTP for small REST partners with OpenAPI client generation.
Where do integration tests live?
Service module tests with HTTP mock (nock, MSW) or vendor test mode.
Circuit breaker needed?
Yes for hard dependencies in request path - see Circuit Breakers.
Queue vendor calls?
Prefer async queue when user does not need synchronous result (email, PDF gen).
Multiple Stripe accounts?
Stripe Connect or per-tenant API key map - never global key for connected accounts without design.
NestJS HttpModule?
Wrap in injectable service; same timeout and error mapping rules.
Version pinning SDK?
Pin major SDK versions; read vendor changelog before bump.
Secrets in logs?
Redact authorization headers in Pino serializers.
Related
- Stripe, Twilio & SDK Patterns - vendor patterns
- Webhook Verification - inbound security
- Retry & Outbound Resilience - backoff
- Integration Failure Playbook - outages
- Integrations Best Practices - checklist
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.