Skip to content
Back to skills

Api Architect

ASecurity

Design and implement production APIs — RESTful and GraphQL. Covers API design principles, endpoint architecture, authentication flows (OAuth2, JWT, API keys), webhook handlers, third-party API integration, rate limiting, retry logic, API versioning, error standardization, and OpenAPI documentation. Use when designing APIs, integrating external services, or building API clients.

  • 67 stars
  • 0 votes
  • 1 copy
  • 21 views
  • Added May 26, 2026
developmentexpressdebuggingapidocumentation

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add RaheesAhmed/SajiCode --skill api-architect --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Api Architect?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Api Architect
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/raheesahmed-api-architect/badge)](https://www.skillsdirectory.com/skills/raheesahmed-api-architect)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: api-architect
description: Design and implement production APIs — RESTful and GraphQL. Covers API design principles, endpoint architecture, authentication flows (OAuth2, JWT, API keys), webhook handlers, third-party API integration, rate limiting, retry logic, API versioning, error standardization, and OpenAPI documentation. Use when designing APIs, integrating external services, or building API clients.
---

# API Architecture

## REST API Design

### URL Structure
```
GET    /api/v1/users          → List users (paginated)
GET    /api/v1/users/:id      → Get single user
POST   /api/v1/users          → Create user
PATCH  /api/v1/users/:id      → Partial update user
DELETE /api/v1/users/:id      → Delete user
GET    /api/v1/users/:id/posts → List user's posts (nested resource)
```

### Response Format
```ts
// Success response
{ "data": { "id": "123", "name": "John" }, "meta": { "requestId": "req_abc" } }

// List response with pagination
{ "data": [...], "meta": { "total": 100, "page": 1, "perPage": 20, "totalPages": 5 } }

// Error response
{ "error": { "code": "VALIDATION_ERROR", "message": "Email is invalid", "details": [...] } }
```

### Status Code Map
| Code | Usage |
|------|-------|
| 200 | Success (GET, PATCH) |
| 201 | Created (POST) |
| 204 | No content (DELETE) |
| 400 | Validation error |
| 401 | Not authenticated |
| 403 | Not authorized |
| 404 | Resource not found |
| 409 | Conflict (duplicate) |
| 422 | Unprocessable entity |
| 429 | Rate limited |
| 500 | Server error |

## Authentication Flows

### JWT Bearer Token
```ts
// Login → returns access + refresh tokens
app.post("/api/auth/login", async (req, res) => {
  const { email, password } = LoginSchema.parse(req.body);
  const user = await userService.authenticate(email, password);
  if (!user) return res.status(401).json({ error: { code: "INVALID_CREDENTIALS", message: "Invalid email or password" } });

  const accessToken = jwt.sign({ sub: user.id, role: user.role }, JWT_SECRET, { expiresIn: "15m" });
  const refreshToken = jwt.sign({ sub: user.id, type: "refresh" }, JWT_REFRESH_SECRET, { expiresIn: "7d" });

  res.json({ data: { accessToken, refreshToken, user: { id: user.id, email: user.email } } });
});

// Auth middleware
function requireAuth(req: Request, res: Response, next: NextFunction) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) return res.status(401).json({ error: { code: "UNAUTHORIZED", message: "Token required" } });

  try {
    const payload = jwt.verify(token, JWT_SECRET) as { sub: string; role: string };
    req.user = payload;
    next();
  } catch {
    res.status(401).json({ error: { code: "TOKEN_EXPIRED", message: "Token expired" } });
  }
}
```

### API Key Authentication
```ts
function requireApiKey(req: Request, res: Response, next: NextFunction) {
  const apiKey = req.headers["x-api-key"] as string;
  if (!apiKey) return res.status(401).json({ error: { code: "API_KEY_REQUIRED", message: "x-api-key header required" } });

  const client = await apiKeyService.validate(apiKey);
  if (!client) return res.status(401).json({ error: { code: "INVALID_API_KEY", message: "Invalid API key" } });

  req.client = client;
  next();
}
```

## Webhook Handler
```ts
import crypto from "crypto";

app.post("/api/webhooks/stripe", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["stripe-signature"] as string;
  const payload = req.body;

  // Verify webhook signature
  const expectedSig = crypto.createHmac("sha256", WEBHOOK_SECRET).update(payload).digest("hex");
  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSig))) {
    return res.status(400).json({ error: { code: "INVALID_SIGNATURE" } });
  }

  const event = JSON.parse(payload.toString());
  switch (event.type) {
    case "checkout.session.completed":
      handleCheckoutComplete(event.data.object);
      break;
    case "invoice.payment_failed":
      handlePaymentFailed(event.data.object);
      break;
  }

  res.json({ received: true });
});
```

## Third-Party API Client
```ts
class ApiClient {
  private baseUrl: string;
  private apiKey: string;

  constructor(baseUrl: string, apiKey: string) {
    this.baseUrl = baseUrl;
    this.apiKey = apiKey;
  }

  private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10000);

    try {
      const response = await fetch(`${this.baseUrl}${path}`, {
        method,
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${this.apiKey}`,
        },
        body: body ? JSON.stringify(body) : undefined,
        signal: controller.signal,
      });

      clearTimeout(timeout);

      if (response.status === 429) {
        const retryAfter = parseInt(response.headers.get("retry-after") || "5");
        await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
        return this.request(method, path, body);
      }

      if (!response.ok) {
        const error = await response.json().catch(() => ({}));
        throw new ApiError(`${method} ${path} failed: ${response.status}`, response.status, error);
      }

      return response.json() as Promise<T>;
    } finally {
      clearTimeout(timeout);
    }
  }

  get<T>(path: string) { return this.request<T>("GET", path); }
  post<T>(path: string, body: unknown) { return this.request<T>("POST", path, body); }
  patch<T>(path: string, body: unknown) { return this.request<T>("PATCH", path, body); }
  delete<T>(path: string) { return this.request<T>("DELETE", path); }
}
```

## Rate Limiting Pattern
```ts
import rateLimit from "express-rate-limit";

// Tiered rate limiting
const publicLimiter = rateLimit({ windowMs: 60000, max: 30 });
const authenticatedLimiter = rateLimit({ windowMs: 60000, max: 100 });
const webhookLimiter = rateLimit({ windowMs: 60000, max: 500 });

app.use("/api/public", publicLimiter);
app.use("/api/v1", requireAuth, authenticatedLimiter);
app.use("/api/webhooks", webhookLimiter);
```

## API Versioning Strategy
```
Option 1: URL versioning (recommended for REST)
  /api/v1/users → /api/v2/users

Option 2: Header versioning
  Accept: application/vnd.myapp.v2+json

Rules:
- Support previous version for minimum 6 months
- Document all breaking changes in changelog
- Use deprecation headers on old versions
```

## Best Practices
- Validate ALL inputs with Zod schemas
- Use consistent error response format across all endpoints
- Implement request ID tracing for debugging
- Log all API calls with method, path, status, duration
- Use cursor-based pagination for large datasets
- Implement idempotency keys for POST/PATCH operations
- Set CORS headers explicitly — never use `origin: "*"` in production
- Document every endpoint with OpenAPI/Swagger

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.