Opinionated TypeScript / Node.js coding standards for building secure, scalable third-party API integrations and proxy services.
You know the drill. What starts as a "simple" third-party integration quickly spirals into a maintenance nightmare of undocumented endpoints, brittle retry logic, and leaked credentials. These Cursor Rules transform chaotic API integrations into rock-solid, production-ready services that scale.
Every developer has been there: integrating with external APIs that change without warning, handling rate limits inconsistently, and debugging authentication flows that fail silently. You're dealing with:
Traditional approaches treat API integrations as afterthoughts, leading to technical debt that compounds with every new service you connect.
These rules establish an API-first methodology that treats external integrations as first-class citizens in your architecture. Built specifically for TypeScript and Node.js, they enforce patterns that eliminate the most common integration failures before they reach production.
What makes this different:
Here's how your integration layer transforms:
Before: Monolithic API handlers mixing concerns
// Fragile, hard to test, security risks
app.post('/sync', async (req, res) => {
const data = await fetch('https://api.vendor.com/data', {
headers: { 'Authorization': process.env.API_KEY } // Leaked in logs
});
// No validation, no retry logic, no error handling
res.json(data);
});
After: Composable, secure, resilient services
export const handler = middy(syncHandler)
.use(traceId())
.use(validator({ eventSchema: SyncRequestSchema }))
.use(errorLogger())
.use(httpErrorHandler());
async function syncHandler(event: APIGatewayEvent) {
const data = await getJson(vendorUrl, VendorResponseSchema);
return { statusCode: 200, body: JSON.stringify(data) };
}
Runtime validation with Zod means your TypeScript types stay synchronized with actual API responses. No more debugging type mismatches at 2 AM.
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
created_at: z.string().datetime()
});
// Automatic validation + type inference
const user = await getJson('/users/123', UserSchema);
// user is fully typed, validation guaranteed
Built-in OAuth2 token refresh and rotation eliminate the most common integration failures. Your services stay connected even when tokens expire.
Contract testing with Pact and automated Postman collections mean your tests actually reflect real API behavior. LocalStack integration tests run consistently in CI.
Every request gets a trace ID that follows through logs, metrics, and errors. Find the root cause in minutes, not hours.
// Automatic trace propagation
logger.info('Processing webhook', {
traceId,
userId: event.userId,
vendor: 'stripe'
});
Old way: Week-long integration with manual testing and deployment anxiety
With these rules:
Total time: Under 1 hour for production-ready integration.
Old way: Production errors, manual fixes, service downtime
With these rules: Exponential backoff with jitter is built-in. Rate limit changes are handled automatically:
// Automatic retry with exponential backoff
const data = await retry(() =>
vendorClient.getData(params),
{ maxAttempts: 5, baseDelay: 1000 }
);
Old way: Scrambling to find and rotate hardcoded credentials
With these rules:
npm install zod @aws-lambda-powertools/logger @aws-lambda-powertools/metrics middy
npm install -D @types/aws-lambda jest
src/
├── handlers/ # Lambda entry points
├── services/ # Business logic
├── integrations/ # Third-party API wrappers
├── models/ # Zod schemas
└── utils/ # Shared helpers
// src/models/vendor-schemas.ts
export const ProductSchema = z.object({
id: z.string(),
name: z.string(),
price: z.number().positive()
});
// src/services/product-service.ts
export async function getProduct(id: string) {
return getJson(`/products/${id}`, ProductSchema);
}
// src/handlers/get-product.ts
export const handler = middy(getProductHandler)
.use(traceId())
.use(validator({ eventSchema: GetProductRequestSchema }))
.use(httpErrorHandler());
Your SAM template includes automatic security best practices:
Integration Development Speed: 5x faster from contract to production
Incident Resolution Time: 75% reduction
Security Posture: Enterprise-grade by default
Code Quality: Consistent across teams
These rules don't just solve today's API integration challenges—they establish patterns that scale with your team and product. Every new integration becomes faster and more reliable than the last.
Your next third-party integration could be your most robust yet. Stop fighting integration complexity and start building the API layer your application deserves.
The only question is: which integration will you transform first?
You are an expert in TypeScript, Node.js (>=18), REST, GraphQL, gRPC, OpenAPI, AWS Lambda, Serverless, and low-/no-code integration tooling (Postman, Zapier, Make, Workato).
Key Principles
- API-first mindset: design contract (OpenAPI/GraphQL SDL/Proto) before writing code.
- Prefer composable, serverless functions. Each Lambda does one job.
- Fail fast: validate inputs immediately, short-circuit on error.
- Security by default: TLS 1.3, strict auth, Zero-Trust, least privilege IAM.
- Idempotency: every public mutation endpoint must accept an Idempotency-Key header.
- Observability built-in: structured JSON logs, trace IDs propagated via `x-trace-id`.
- Automation friendly: make every action testable from CLI & CI (Postman/Newman).
- Convention over configuration: same directory & file patterns across repos.
TypeScript
- Use `strict`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`.
- Prefer `interface` for contracts, `type` for unions & mapped types.
- Always return explicit types—no `any`, no implicit `any`.
- Use `async/await`; never mix with `.then` chains in the same scope.
- Promise rejections must be typed with `Error` or subclasses.
- Default export prohibited. Use named exports only.
- File naming: `kebab-case.ts`; tests: `*.spec.ts`.
- Path aliases via `tsconfig.json` (`@core/*`, `@services/*`, `@types/*`). No relative hell (`../../../`).
- Example helper:
```ts
export async function getJson<T>(url: string, schema: z.ZodSchema<T>): Promise<T> {
const res = await fetch(url, { headers: defaultHeaders });
if (!res.ok) throw new HttpError(res.status, await res.text());
const data = await res.json();
return schema.parse(data);
}
```
Error Handling and Validation
- Use `zod` for runtime validation of all inbound & outbound payloads.
- Custom `HttpError extends Error { status: number; details?: unknown; }` shared across repo.
- Error flow:
1. Detect & throw early in service layer.
2. Map to friendly message in controller/handler.
3. Mask sensitive info before logging.
- Do NOT leak stack traces or raw upstream errors to clients.
- Rate-limit handling: on `429` implement exponential backoff (base 2, max 5 retries) + jitter.
- Retry only idempotent operations (GET, PUT, PATCH with Idempotency-Key).
Framework-Specific Rules (AWS Lambda + API Gateway)
- Thin Lambda: import business logic from `@services`, keep handler < 40 LOC.
- Handler pattern:
```ts
export const handler = middy(coreHandler)
.use(traceId())
.use(jsonBodyParser())
.use(validator({ eventSchema }))
.use(errorLogger())
.use(httpErrorHandler());
```
- All environment variables prefixed with `APP_`; validate them on cold start.
- Deploy with SAM or CDK; stacks are version-controlled.
- Stage-specific config in `env/${stage}.ts`. Never inline secrets.
Additional Sections
Security
- Enforce HTTPS via HSTS; refuse plaintext (`x-forwarded-proto != https`).
- Use TLS 1.3, prefer AEAD ciphers.
- Store secrets in AWS Secrets Manager; rotate every 90 days via Lambda rotation.
- Auth to third-party APIs with short-lived OAuth2 tokens; implement automatic refresh.
- IP allow-list / geo-fence via API Gateway resource policies when required.
- WebSockets: require signed JWT in connection URL + mutual TLS where supported.
Testing
- Unit tests: Jest with 90% branch coverage gate.
- Contract tests: use `pact` for consumer-driven contracts against third-party mocks.
- Integration tests: spin up LocalStack + wiremock in CI.
- Security tests: run `npm run test:zap` (OWASP ZAP) weekly.
- Load tests: k6 scripts committed under `/tests/perf`.
Performance & Scalability
- Prefer `GET /resource?fields=…` or GraphQL selection to avoid over-fetching.
- Cache read-only endpoints in CloudFront, set `Cache-Control: public, max-age=60`.
- Use bulk/batch endpoints when pagination > 500 calls.
- Async tasks (e.g., ETL) queued in SQS; Lambda consumers scale on demand.
- gRPC for internal microservice calls (>100 rps, latency < 30 ms).
Documentation & Monitoring
- Auto-generate OpenAPI via `tsoa` and publish to Stoplight.
- Postman collection committed and kept in CI; use Postman Flows for E2E.
- All logs structured: `{ level, msg, traceId, userId?, error? }`.
- Metrics: embed `@aws-lambda-powertools/metrics`; alarms on p95 latency, error-rate >2%.
Low-/No-Code Integration Guidance
- Provide Zapier Private App with mandatory OAuth2 flow.
- Expose clear triggers/actions. Use REST hooks for change events.
- Include sample CURL + Postman snippets in docs.
- Build reusable Workato connector and publish to community catalog.
Project Structure
```
├── src
│ ├── handlers # Lambda entrypoints
│ ├── services # Pure business logic
│ ├── integrations # Third-party SDK wrappers
│ ├── models # zod schemas & TypeScript types
│ ├── utils # helpers (logging, http, auth)
│ └── config # env & constants
└── tests
├── unit
├── integration
└── perf
```
Common Pitfalls & How to Avoid Them
- "Chatty" APIs → batch & cache results; use GraphQL where dynamic fields needed.
- Leaking credentials in logs → always redact using logger serializer.
- Long-running Lambdas (>15 min) → move to Step Functions or container tasks.
- Non-idempotent retries → enforce Idempotency-Key & proper HTTP verbs.
Ready-to-Use Snippets
- Exponential backoff helper:
```ts
export async function retry<T>(fn: () => Promise<T>, attempts = 5) {
for (let i = 0; i < attempts; i++) {
try { return await fn(); }
catch (err: unknown) {
if (!(err instanceof HttpError) || err.status !== 429 || i === attempts - 1)
throw err;
await wait(Math.pow(2, i) * 100 + Math.random() * 100);
}
}
}
```
- Idempotent write via Axios:
```ts
await axios.post(url, payload, {
headers: {
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json'
}
});
```
Follow these rules to produce secure, maintainable, and highly scalable third-party API integrations that stand up to modern compliance and performance requirements.