# API timeout handling configured

- **Pattern:** `ab-002211` (`saas-api-design.api-docs.api-timeout-handling`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002211
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002211)

## Why it matters

Route handlers that call external services (AI APIs, payment processors, email providers, slow database queries) with no timeout configuration can hang for the full platform timeout — 30+ seconds on Vercel — blocking the response and consuming a serverless function invocation slot. In serverless environments, this does not just affect the single user: it contributes to concurrency exhaustion (CWE-400, CWE-770) and drives up cost proportionally with hang duration. ISO-25010:2011 reliability.fault-tolerance requires that slow dependencies do not cascade into application-level failures.

## Severity rationale

Low because most platforms impose a hard ceiling, but hanging handlers degrade UX, inflate cost, and under high load can exhaust concurrency limits for all users.

## Remediation

Add timeout handling in `app/api/` route handlers for every external call using `AbortController`, and declare `maxDuration` in route files that need extended execution time:

```ts
// app/api/ai/generate/route.ts
export const maxDuration = 30 // seconds — declare for Vercel

export async function POST(req: Request) {
  const controller = new AbortController()
  const timeout = setTimeout(() => controller.abort(), 8000) // 8s per-call limit

  try {
    const response = await fetch(AI_API_URL, {
      method: 'POST',
      body: JSON.stringify(payload),
      signal: controller.signal,
    })
    clearTimeout(timeout)
    return apiSuccess(await response.json())
  } catch (err) {
    if (err instanceof Error && err.name === 'AbortError') {
      return apiError('TIMEOUT', 'Upstream service timed out', 504)
    }
    throw err
  }
}
```

Return 504 (Gateway Timeout) rather than letting the connection hang until the platform terminates it.

## Detection

- **ID:** `api-timeout-handling`
- **Severity:** `low`
- **What to look for:** Enumerate all API routes that call external services. Count those with and without timeout handling. Look for: Next.js `maxDuration` export in route handlers (`export const maxDuration = 30`), Vercel function timeout config in `vercel.json`, Express/Fastify server timeout settings, `AbortController` usage in fetch calls within handlers, explicit timeout wrappers around slow operations (database queries, external API calls). Also check: do handlers that call external services have per-call timeouts, or could a slow downstream service cause handlers to hang indefinitely?
- **Pass criteria:** At least 100% of API routes that may perform slow operations (database queries, external API calls, file processing) either declare an explicit timeout limit or use timeout-aware patterns (AbortController, per-call timeouts on external calls).
- **Fail criteria:** Handlers that call external services or run potentially slow operations have no timeout configuration and could hang indefinitely until the platform's default timeout (which may be 30+ seconds, creating a poor user experience and resource waste).
- **Skip (N/A) when:** All API routes are trivial (simple database reads with no external calls) and the platform enforces a short default timeout. Signal: no external API calls in route handlers and hosting platform imposes a hard timeout per the detected config.
- **Detail on fail:** Example: `POST /api/ai/generate has no timeout handling`. Note which handlers lack timeout handling (e.g., "POST /api/ai/generate calls external AI API with no AbortController timeout; no maxDuration export on any route handler"). Max 500 chars.
- **Remediation:** Add timeout handling in `app/api/` route files to routes that perform slow operations. In Next.js App Router, set `export const maxDuration = 30` (seconds) at the top of route files that need extended time, and use `export const runtime = 'nodejs'` for routes that need longer than 25 seconds on Vercel's hobby plan. For external API calls within handlers, wrap them with an AbortController timeout: `const controller = new AbortController(); setTimeout(() => controller.abort(), 5000); await fetch(url, { signal: controller.signal })`. For database queries, use query-level timeouts if your ORM supports them. Return a 504 status code when a timeout occurs rather than letting the connection hang.

## External references

- cwe CWE-400
- iso-25010 reliability.fault-tolerance
- cwe CWE-770

Taxons: error-resilience, cost-efficiency

HTML version: https://auditbuffet.com/patterns/ab-002211
