API Integration Skill Example

A skill for designing and implementing robust API integrations.

Use Case

This skill helps with:

  • RESTful API design
  • Error handling patterns
  • Authentication implementations
  • Rate limiting and retry logic

Complete SKILL.md

markdown
---
name: API Integration Best Practices
description: Design and implement robust, maintainable API integrations
version: 1.0.0
author: API Design Community
platforms:
  - claude-code
  - codex
categories:
  - development
  - backend
tags:
  - api
  - rest
  - integration
---

# API Integration Best Practices

## Design Principles

1. **Consistency**: Use consistent naming and patterns
2. **Resilience**: Handle failures gracefully
3. **Security**: Protect data in transit and at rest
4. **Observability**: Log and monitor effectively

## RESTful Design

### URL Structure

\`\`\`
GET    /api/v1/users          # List users
GET    /api/v1/users/:id      # Get user
POST   /api/v1/users          # Create user
PATCH  /api/v1/users/:id      # Update user
DELETE /api/v1/users/:id      # Delete user
\`\`\`

### Naming Conventions
- Use plural nouns for collections
- Use lowercase with hyphens
- Avoid verbs in URLs (actions are HTTP methods)
- Version your API (v1, v2)

## Response Formats

### Success Response

\`\`\`json
{
  "data": {
    "id": "123",
    "type": "user",
    "attributes": {
      "name": "John Doe",
      "email": "john@example.com"
    }
  },
  "meta": {
    "requestId": "req_abc123"
  }
}
\`\`\`

### Error Response

\`\`\`json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Email is required",
    "details": [
      {
        "field": "email",
        "message": "This field is required"
      }
    ]
  },
  "meta": {
    "requestId": "req_abc123"
  }
}
\`\`\`

### Pagination

\`\`\`json
{
  "data": [...],
  "pagination": {
    "page": 1,
    "perPage": 20,
    "total": 100,
    "totalPages": 5
  },
  "links": {
    "self": "/api/v1/users?page=1",
    "next": "/api/v1/users?page=2",
    "prev": null
  }
}
\`\`\`

## HTTP Status Codes

| Code | Meaning | When to Use |
|------|---------|-------------|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Invalid input |
| 401 | Unauthorized | Missing/invalid auth |
| 403 | Forbidden | Valid auth, no permission |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable | Validation errors |
| 429 | Too Many Requests | Rate limited |
| 500 | Server Error | Unexpected error |

## Error Handling

### Client Implementation

\`\`\`typescript
async function fetchWithRetry<T>(
  url: string,
  options: RequestInit,
  maxRetries = 3
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After');
        await sleep(parseInt(retryAfter || '1') * 1000);
        continue;
      }

      if (!response.ok) {
        throw new ApiError(response.status, await response.json());
      }

      return response.json();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await sleep(Math.pow(2, attempt) * 1000); // Exponential backoff
    }
  }
}
\`\`\`

## Authentication

### Bearer Token

\`\`\`typescript
const response = await fetch('/api/resource', {
  headers: {
    'Authorization': \`Bearer \${accessToken}\`,
    'Content-Type': 'application/json'
  }
});
\`\`\`

### Token Refresh

\`\`\`typescript
async function refreshAccessToken(refreshToken: string) {
  const response = await fetch('/api/auth/refresh', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refreshToken })
  });

  if (!response.ok) {
    throw new AuthError('Token refresh failed');
  }

  return response.json();
}
\`\`\`

## Rate Limiting

### Client-Side Handling

\`\`\`typescript
class RateLimiter {
  private queue: Array<() => Promise<void>> = [];
  private processing = false;
  private requestsPerSecond: number;

  constructor(requestsPerSecond = 10) {
    this.requestsPerSecond = requestsPerSecond;
  }

  async throttle<T>(fn: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.queue.push(async () => {
        try {
          resolve(await fn());
        } catch (error) {
          reject(error);
        }
      });
      this.processQueue();
    });
  }

  private async processQueue() {
    if (this.processing) return;
    this.processing = true;

    while (this.queue.length > 0) {
      const fn = this.queue.shift()!;
      await fn();
      await sleep(1000 / this.requestsPerSecond);
    }

    this.processing = false;
  }
}
\`\`\`

## Security Checklist

- [ ] Use HTTPS everywhere
- [ ] Validate all input
- [ ] Sanitize output
- [ ] Implement rate limiting
- [ ] Use secure token storage
- [ ] Log security events
- [ ] Rotate credentials regularly

Next Steps