# Defensive Api Handling

> Safely handles API responses to prevent crashes from malformed JSON, HTML error pages, or unexpected response types. Use any time calling API endpoints.

- **Type:** Skill
- **Install:** `agentstack add skill-bradtaylorsf-alphaagent-team-defensive-api-handling`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [bradtaylorsf](https://agentstack.voostack.com/s/bradtaylorsf)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [bradtaylorsf](https://github.com/bradtaylorsf)
- **Source:** https://github.com/bradtaylorsf/alphaagent-team/tree/main/plugins/aai-core/skills/defensive-api-handling

## Install

```sh
agentstack add skill-bradtaylorsf-alphaagent-team-defensive-api-handling
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Defensive API Handling

**Purpose**: Safely handle API responses to prevent crashes

**When to Use**: Any time you call an API endpoint (in tests or production code)

---

## Never Assume API Returns JSON

APIs can return:
- JSON (expected)
- HTML error pages
- Empty responses
- Malformed JSON
- Non-200 status codes

**Your code must handle ALL of these safely.**

---

## Pattern 1: Safe JSON Parsing

```typescript
async function safeJsonParse(response: Response): Promise {
  // 1. Check status code
  if (!response.ok) {
    console.warn(`API returned ${response.status} ${response.statusText}`);
    return null;
  }

  // 2. Check content type
  const contentType = response.headers.get('content-type');
  if (!contentType?.includes('application/json')) {
    console.warn(`Expected JSON, got ${contentType}`);
    const text = await response.text();
    console.warn(`Response body: ${text.slice(0, 200)}`);
    return null;
  }

  // 3. Try to parse
  try {
    return await response.json();
  } catch (error) {
    console.warn('Failed to parse JSON:', error.message);
    return null;
  }
}
```

---

## Pattern 2: Fetch with Timeout

```typescript
async function fetchWithTimeout(
  url: string,
  options: RequestInit = {},
  timeoutMs: number = 10000
): Promise {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    return response;
  } finally {
    clearTimeout(timeout);
  }
}
```

---

## Pattern 3: Retry Logic

```typescript
async function fetchWithRetry(
  url: string,
  options: RequestInit = {},
  maxRetries: number = 3,
  delayMs: number = 1000
): Promise {
  for (let attempt = 1; attempt = 500 && attempt  setTimeout(resolve, delayMs * attempt));
        continue;
      }

      return response;
    } catch (error) {
      if (attempt === maxRetries) {
        console.error(`All ${maxRetries} attempts failed:`, error.message);
        return null;
      }
      await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
    }
  }
  return null;
}
```

---

## Pattern 4: Type-Safe API Responses

```typescript
// Define expected response types
interface ApiResponse {
  data?: T;
  error?: string;
}

// Type guard function
function isApiResponse(
  value: unknown,
  dataGuard: (v: unknown) => v is T
): value is ApiResponse {
  if (typeof value !== 'object' || value === null) return false;
  const obj = value as Record;

  if ('error' in obj && typeof obj.error !== 'string') return false;
  if ('data' in obj && !dataGuard(obj.data)) return false;

  return true;
}

// Safe API call with type checking
async function safeApiCall(
  url: string,
  dataGuard: (v: unknown) => v is T
): Promise {
  const response = await fetchWithRetry(url);
  if (!response) return null;

  const json = await safeJsonParse(response);
  if (!json) return null;

  if (!isApiResponse(json, dataGuard)) {
    console.warn('Unexpected response structure:', json);
    return null;
  }

  if (json.error) {
    console.warn('API returned error:', json.error);
    return null;
  }

  return json.data ?? null;
}
```

---

## Pattern 5: Error Boundary for API Calls

```typescript
async function withApiErrorBoundary(
  operation: () => Promise,
  context: string,
  fallback: T
): Promise {
  try {
    return await operation();
  } catch (error) {
    console.error(`[${context}] API error:`, error.message);

    // Log additional context
    if (error instanceof TypeError && error.message.includes('fetch')) {
      console.error(`Network error - check connectivity`);
    }

    return fallback;
  }
}

// Usage
const users = await withApiErrorBoundary(
  () => fetchUsers(),
  'fetchUsers',
  [] // fallback to empty array
);
```

---

## Anti-Patterns to Avoid

```typescript
// ❌ BAD: Assumes JSON, no error handling
const data = await fetch('/api/users').then(r => r.json());

// ❌ BAD: No timeout
const response = await fetch('/api/slow-endpoint');

// ❌ BAD: Trusts response structure
const users = data.users.map(u => u.name);

// ❌ BAD: Silent failures
try {
  return await fetch('/api/users').then(r => r.json());
} catch {
  return [];  // Caller doesn't know it failed
}
```

---

## Success Criteria

- Zero `SyntaxError: Unexpected token '<'` errors
- Zero `Cannot read property of undefined` errors
- Cleanup hooks never cause test suite to crash
- Tests skip gracefully when API unavailable
- All API calls have timeouts
- Errors are logged with context

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [bradtaylorsf](https://github.com/bradtaylorsf)
- **Source:** [bradtaylorsf/alphaagent-team](https://github.com/bradtaylorsf/alphaagent-team)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-bradtaylorsf-alphaagent-team-defensive-api-handling
- Seller: https://agentstack.voostack.com/s/bradtaylorsf
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
