home / skills / secondsky / claude-skills / api-error-handling

This skill enforces standardized API error handling with structured responses, logging, and recovery patterns to improve reliability and observability.

npx playbooks add skill secondsky/claude-skills --skill api-error-handling

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
2.8 KB
---
name: api-error-handling
description: Implements standardized API error responses with proper status codes, logging, and user-friendly messages. Use when building production APIs, implementing error recovery patterns, or integrating error monitoring services.
---

# API Error Handling

Implement robust error handling with standardized responses and proper logging.

## Standard Error Response Format

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "status": 400,
    "requestId": "req_abc123",
    "timestamp": "2025-01-15T10:30:00Z",
    "details": [
      { "field": "email", "message": "Invalid email format" }
    ]
  }
}
```

## Error Class (Node.js)

```javascript
class ApiError extends Error {
  constructor(code, message, status = 500, details = null) {
    super(message);
    this.code = code;
    this.status = status;
    this.details = details;
  }

  static badRequest(message, details) {
    return new ApiError('BAD_REQUEST', message, 400, details);
  }

  static notFound(resource) {
    return new ApiError('NOT_FOUND', `${resource} not found`, 404);
  }

  static unauthorized() {
    return new ApiError('UNAUTHORIZED', 'Authentication required', 401);
  }
}

// Global error handler
app.use((err, req, res, next) => {
  const status = err.status || 500;
  const response = {
    error: {
      code: err.code || 'INTERNAL_ERROR',
      message: status === 500 ? 'Internal server error' : err.message,
      status,
      requestId: req.id
    }
  };

  if (err.details) response.error.details = err.details;
  if (status >= 500) logger.error(err);

  res.status(status).json(response);
});
```

## Circuit Breaker Pattern

```javascript
class CircuitBreaker {
  constructor(threshold = 5, timeout = 30000) {
    this.failures = 0;
    this.threshold = threshold;
    this.timeout = timeout;
    this.state = 'CLOSED';
  }

  async call(fn) {
    if (this.state === 'OPEN') throw new Error('Circuit open');
    try {
      const result = await fn();
      this.failures = 0;
      return result;
    } catch (err) {
      this.failures++;
      if (this.failures >= this.threshold) {
        this.state = 'OPEN';
        setTimeout(() => this.state = 'HALF_OPEN', this.timeout);
      }
      throw err;
    }
  }
}
```

## Additional Implementations

See [references/python-flask.md](references/python-flask.md) for:
- Python Flask error handling with custom exceptions
- Circuit breaker with automatic recovery
- Retry with exponential backoff
- Sentry integration

## Best Practices

- Use consistent error format across all endpoints
- Include request IDs for traceability
- Log errors at appropriate severity levels
- Never expose stack traces to clients
- Distinguish client errors (4xx) from server errors (5xx)
- Provide actionable error messages

Overview

This skill implements standardized API error responses with proper status codes, structured payloads, and integrated logging. It provides a reusable ApiError class, a global error handler, and optional resilience patterns like a circuit breaker for production-ready TypeScript/Node APIs. Use it to make errors predictable, traceable, and safe to return to clients.

How this skill works

The skill defines an ApiError class to represent domain and HTTP errors with code, message, status, requestId, timestamp, and optional details. A global Express-style error handler formats responses, attaches request IDs, hides internal stack traces, and logs server-side failures. A simple CircuitBreaker class is included to stop repeated downstream failures and allow recovery after a timeout.

When to use it

  • Building production REST or GraphQL APIs
  • Standardizing error payloads across services
  • Integrating error monitoring (Sentry, Datadog)
  • Implementing resiliency for external service calls
  • Differentiating client (4xx) vs server (5xx) errors

Best practices

  • Always return a consistent error JSON shape with code, message, status, requestId, and optional details.
  • Attach a unique requestId to every request for tracing across logs and services.
  • Log 5xx errors at error/fatal level and 4xx at warn/info as appropriate; never expose stack traces to clients.
  • Use specific ApiError factory methods (badRequest, notFound, unauthorized) to keep handlers concise.
  • Combine circuit breaker and retry patterns but avoid retries for idempotency-unsafe operations.

Example use cases

  • Validate incoming payloads and return BAD_REQUEST with details per-field for client fixes.
  • Wrap downstream API calls with CircuitBreaker to prevent cascading failures during outages.
  • Throw ApiError.notFound for missing resources and ensure 404 responses include a requestId for support.
  • Capture 5xx errors with logger and send enriched events to Sentry including requestId and user context.
  • Return user-friendly messages for authentication/authorization failures while logging full context internally.

FAQ

How do I include a requestId in responses?

Generate or propagate a requestId in middleware (e.g., from headers or a UUID) and attach it to req; the error handler then includes it in the response error.requestId.

Should I expose error details to clients?

Only include actionable, non-sensitive details for client errors (4xx). Never expose stack traces or internal diagnostics for 5xx errors; use logging and monitoring instead.