All skills
asyrafhussin avatar

/api-design-patterns

@1de3a7a

RESTful API design, error handling, versioning, and best practices. Use when designing APIs, reviewing endpoints, implementing error responses, or setting up API structure. Triggers on "design API", "review API", "REST best practices", or "API patterns".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/api-design-patterns

This session only. Nothing lands on disk.

ruleserror-no-stack-traces.md

≈1.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Never Expose Stack Traces in Production

Impact: CRITICAL (Prevents security vulnerabilities and information disclosure)

Stack traces and internal error details should never be exposed to API clients in production environments, as they reveal implementation details and potential vulnerabilities.

Incorrect

// ❌ Full stack trace in production response
{
  "error": "Cannot read property 'id' of undefined",
  "stack": "TypeError: Cannot read property 'id' of undefined\n    at getUserOrders (/app/src/controllers/orders.js:45:23)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at next (/app/node_modules/express/lib/router/route.js:137:13)\n    at authenticate (/app/src/middleware/auth.js:28:5)\n    at /app/node_modules/express/lib/router/index.js:284:15"
}

// ❌ Database error details exposed
{
  "error": "SequelizeConnectionError: Connection refused to host 'db.internal.company.com' port 5432",
  "sql": "SELECT * FROM users WHERE id = 1 AND deleted_at IS NULL"
}

// ❌ Internal paths and configuration
{
  "error": "ENOENT: no such file or directory, open '/var/app/config/secrets.json'"
}
// ❌ Exposing all error details
app.use((err, req, res, next) => {
  res.status(500).json({
    error: err.message,
    stack: err.stack,  // Never do this in production!
    code: err.code
  });
});

Problems:

  • Stack traces reveal file paths, dependencies, and code structure attackers can exploit
  • Database error messages may expose schemas, connection strings, or query logic
  • Internal file paths reveal server configuration and directory structure
  • Framework and version information helps attackers find known vulnerabilities
  • Violates security standards like PCI-DSS and SOC 2

Correct

// ✅ Secure error handler
const isProduction = process.env.NODE_ENV === 'production';

app.use((err, req, res, next) => {
  // Log full error internally
  logger.error('Request error', {
    error: err.message,
    stack: err.stack,
    requestId: req.id,
    path: req.path,
    method: req.method,
    userId: req.user?.id
  });

  // Determine if error is safe to expose
  const isOperationalError = err.isOperational || err.expose;
  const statusCode = err.statusCode || 500;

  // Build safe response
  const errorResponse = {
    error: {
      code: err.code || 'internal_error',
      message: isOperationalError
        ? err.message
        : 'An unexpected error occurred. Please try again later.',
      requestId: req.id
    }
  };

  // Only include details in development
  if (!isProduction && err.stack) {
    errorResponse.error._debug = {
      message: err.message,
      stack: err.stack.split('\n')
    };
  }

  res.status(statusCode).json(errorResponse);
});

// Custom error class for operational errors
class APIError extends Error {
  constructor(message, statusCode = 500, code = 'internal_error') {
    super(message);
    this.statusCode = statusCode;
    this.code = code;
    this.isOperational = true; // Safe to expose
  }
}

// Database error handling
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await db.findUser(req.params.id);
    if (!user) {
      throw new APIError('User not found', 404, 'resource_not_found');
    }
    res.json(user);
  } catch (error) {
    if (error instanceof APIError) {
      return next(error);
    }

    // Log the actual database error
    logger.error('Database error', {
      error: error.message,
      stack: error.stack,
      query: 'findUser',
      params: { id: req.params.id }
    });

    // Return generic error to client
    next(new APIError(
      'Unable to retrieve user information',
      500,
      'service_error'
    ));
  }
});
# ✅ FastAPI with secure error handling
import logging
import traceback
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import os

app = FastAPI()
logger = logging.getLogger(__name__)
IS_PRODUCTION = os.getenv("ENVIRONMENT") == "production"

class APIError(Exception):
    def __init__(self, message: str, status_code: int = 500, code: str = "internal_error"):
        self.message = message
        self.status_code = status_code
        self.code = code
        self.is_operational = True

@app.exception_handler(APIError)
async def api_error_handler(request: Request, exc: APIError):
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": {
                "code": exc.code,
                "message": exc.message,
                "request_id": request.state.request_id
            }
        }
    )

@app.exception_handler(Exception)
async def generic_error_handler(request: Request, exc: Exception):
    # Log full error internally
    logger.error(
        "Unhandled exception",
        extra={
            "error": str(exc),
            "traceback": traceback.format_exc(),
            "request_id": request.state.request_id,
            "path": request.url.path,
            "method": request.method
        }
    )

    # Return safe response
    content = {
        "error": {
            "code": "internal_error",
            "message": "An unexpected error occurred. Please try again later.",
            "request_id": request.state.request_id
        }
    }

    # Include debug info only in development
    if not IS_PRODUCTION:
        content["error"]["_debug"] = {
            "type": type(exc).__name__,
            "message": str(exc),
            "traceback": traceback.format_exc().split("\n")
        }

    return JSONResponse(status_code=500, content=content)
// ✅ Production error response (safe)
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again later.",
    "requestId": "req-abc123"
  }
}

// ✅ Development error response (with debug info)
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again later.",
    "requestId": "req-abc123",
    "_debug": {
      "type": "TypeError",
      "message": "Cannot read property 'id' of undefined",
      "traceback": [
        "Traceback (most recent call last):",
        "  File \"app.py\", line 45, in get_user",
        "    return user.id",
        "TypeError: Cannot read property 'id' of undefined"
      ]
    }
  }
}

What to Log vs. What to Return

Information Log Internally Return to Client
Error message Yes Generic only
Stack trace Yes Never in production
SQL queries Yes Never
File paths Yes Never
Internal IPs Yes Never
Request ID Yes Yes
Error code Yes Yes
User ID Yes No
Timestamps Yes Optional

Benefits:

  • Prevents attackers from exploiting revealed file paths, dependencies, and code structure
  • Protects database schemas, API keys, and other secrets from leaking
  • Hides framework versions that could expose known vulnerabilities
  • Clean error messages present a professional API to consumers
  • Meets compliance requirements for PCI-DSS, SOC 2, and similar standards
  • Request IDs enable correlation between client reports and internal logs

Reference: OWASP Improper Error Handling

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill is a comprehensive documentation library for RESTful API design patterns. It provides best practices for resource design, error handling, security, and documentation. The skill contains no executable code, malicious instructions, or hidden functionality, and its references are restricted to well-known technical documentation sources.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    4/28 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 1de3a7a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
metadata
{
  "author": "agent-skills",
  "version": "2.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/api-design-patterns