Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Api Design

ASecurity

Design clean, scalable, and maintainable REST and GraphQL APIs following industry best practices. Use when designing public or internal APIs, planning endpoint structures, defining request/response contracts, establishing versioning strategies, implementing authentication patterns, designing data models, creating API documentation, ensuring consistent error handling, optimizing for performance, or establishing service contracts between microservices.

3 stars
0 votes
0 copies
1 views
Added 9/20/2026
developmenttypescriptgosqlexpressapidatabasesecurityperformancedocumentation

Works with

cursorcliapi

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add Fabric-Pro/fabric-oss --skill api-design --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Api Design?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Api Design
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fabric-pro-api-design/badge)](https://www.skillsdirectory.com/skills/fabric-pro-api-design)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: api-design
description: Design clean, scalable, and maintainable REST and GraphQL APIs following industry best practices. Use when designing public or internal APIs, planning endpoint structures, defining request/response contracts, establishing versioning strategies, implementing authentication patterns, designing data models, creating API documentation, ensuring consistent error handling, optimizing for performance, or establishing service contracts between microservices.
---

# API Design - Building Clean, Scalable REST & GraphQL APIs

## When to use this skill

- Designing new REST or GraphQL APIs from scratch.
- Planning endpoint structures and URL patterns
- Defining request/response contracts and data schemas
- Establishing API versioning and deprecation strategies
- Implementing authentication and authorization patterns
- Creating API documentation (OpenAPI/Swagger)
- Designing error response formats and status codes
- Planning pagination, filtering, and sorting strategies
- Establishing rate limiting and throttling policies
- Designing webhooks or event-driven integrations
- Creating service contracts for microservices
- Optimizing API performance and caching strategies

## When to use this skill

- Designing public or internal APIs, planning endpoints, defining contracts between services.
- When working on related tasks or features
- During development that requires this expertise

**Use when**: Designing public or internal APIs, planning endpoints, defining contracts between services.

## Core Principles

1. **Consistency** - Predictable patterns across endpoints
2. **Simplicity** - Easy to understand and use
3. **Versioning** - Support evolution without breaking clients
4. **Security** - Authentication, authorization, rate limiting
5. **Documentation** - Clear, up-to-date API specs

## REST API Design

### 1. **Resource-Based URLs**

```
✅ Good - Nouns, not verbs
GET    /users              - List users
GET    /users/:id          - Get specific user
POST   /users              - Create user
PUT    /users/:id          - Update user (full replace)
PATCH  /users/:id          - Update user (partial)
DELETE /users/:id          - Delete user

GET    /users/:id/posts    - Get user's posts
POST   /users/:id/posts    - Create post for user

❌ Bad - Verbs in URLs
GET    /getUsers
POST   /createUser
POST   /users/delete/:id
```

### 2. **HTTP Methods & Status Codes**

```typescript
// ✅ Proper HTTP method usage
app.get('/users', async (req, res) => {
  const users = await db.users.findAll();
  res.json(users); // 200 OK
});

app.post('/users', async (req, res) => {
  const user = await db.users.create(req.body);
  res.status(201) // 201 Created
     .location(`/users/${user.id}`)
     .json(user);
});

app.put('/users/:id', async (req, res) => {
  const user = await db.users.update(req.params.id, req.body);
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
  res.json(user); // 200 OK
});

app.delete('/users/:id', async (req, res) => {
  await db.users.delete(req.params.id);
  res.status(204).send(); // 204 No Content
});

// ✅ Common status codes
// 2xx Success
200 OK                  - Request succeeded
201 Created             - Resource created
204 No Content          - Succeeded, no response body

// 4xx Client Errors
400 Bad Request         - Invalid input
401 Unauthorized        - Not authenticated
403 Forbidden           - Authenticated but no permission
404 Not Found           - Resource doesn't exist
409 Conflict            - Duplicate or conflicting state
422 Unprocessable       - Validation failed
429 Too Many Requests   - Rate limited

// 5xx Server Errors
500 Internal Error      - Something broke on server
503 Service Unavailable - Temporarily down
```

### 3. **Request/Response Format**

```typescript
// ✅ Consistent response structure
interface ApiResponse<T> {
  data: T;
  meta?: {
    page?: number;
    limit?: number;
    total?: number;
  };
  links?: {
    self: string;
    next?: string;
    prev?: string;
  };
}

// Success response
{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "john@example.com"
  }
}

// List response with pagination
{
  "data": [
    { "id": "1", "name": "User 1" },
    { "id": "2", "name": "User 2" }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 100
  },
  "links": {
    "self": "/users?page=1",
    "next": "/users?page=2"
  }
}

// Error response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email format",
    "details": [
      {
        "field": "email",
        "message": "Must be valid email"
      }
    ]
  }
}
```

### 4. **Filtering, Sorting, Pagination**

```typescript
// ✅ Query parameters for filtering
GET /users?status=active&role=admin
GET /posts?author=john&tags=tech,programming
GET /products?minPrice=10&maxPrice=100

app.get('/users', async (req, res) => {
  const { status, role, page = 1, limit = 20, sort = 'createdAt' } = req.query;
  
  const query = {};
  if (status) query.status = status;
  if (role) query.role = role;
  
  const users = await db.users.findMany({
    where: query,
    skip: (page - 1) * limit,
    take: limit,
    orderBy: { [sort]: 'desc' }
  });
  
  const total = await db.users.count({ where: query });
  
  res.json({
    data: users,
    meta: { page, limit, total },
    links: {
      self: `/users?page=${page}`,
      next: page * limit < total ? `/users?page=${page + 1}` : null
    }
  });
});

// ✅ Sorting
GET /users?sort=name           - Sort by name ascending
GET /users?sort=-createdAt     - Sort by createdAt descending
GET /users?sort=role,-createdAt - Multiple sort fields

// ✅ Field selection (sparse fieldsets)
GET /users?fields=id,name,email - Only return specified fields
```

### 5. **Versioning**

```typescript
// ✅ URL versioning (most common)
GET /api/v1/users
GET /api/v2/users

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

// ✅ Header versioning
GET /api/users
Headers: { "Accept-Version": "v2" }

// ✅ Deprecation warnings
app.use('/api/v1', (req, res, next) => {
  res.set('X-API-Deprecation', 'v1 will be deprecated on 2024-12-31');
  res.set('X-API-Upgrade', 'See /api/v2 for latest version');
  next();
});
```

### 6. **Authentication & Authorization**

```typescript
// ✅ Bearer token authentication
GET /api/users
Headers: { "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }

function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// ✅ Rate limiting
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100
});

app.use('/api/', limiter);

// ✅ API keys for service-to-service
GET /api/users
Headers: { "X-API-Key": "sk_live_abc123..." }

function requireApiKey(req, res, next) {
  const apiKey = req.get('X-API-Key');
  
  if (!isValidApiKey(apiKey)) {
    return res.status(401).json({ error: 'Invalid API key' });
  }
  
  next();
}
```

### 7. **HATEOAS (Hypermedia)**

```typescript
// ✅ Include related resource links
{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "john@example.com"
  },
  "links": {
    "self": "/users/123",
    "posts": "/users/123/posts",
    "followers": "/users/123/followers"
  }
}

// ✅ Action links for state transitions
{
  "data": {
    "id": "456",
    "status": "pending",
    "amount": 100
  },
  "actions": {
    "approve": {
      "method": "POST",
      "href": "/orders/456/approve"
    },
    "cancel": {
      "method": "DELETE",
      "href": "/orders/456"
    }
  }
}
```

## GraphQL API Design

### 1. **Schema Definition**

```graphql
# ✅ Clear, typed schema
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
  createdAt: DateTime!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  published: Boolean!
}

type Query {
  user(id: ID!): User
  users(limit: Int, offset: Int): [User!]!
  post(id: ID!): Post
  posts(authorId: ID, published: Boolean): [Post!]!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
  
  createPost(input: CreatePostInput!): Post!
  publishPost(id: ID!): Post!
}

input CreateUserInput {
  name: String!
  email: String!
}

input UpdateUserInput {
  name: String
  email: String
}

input CreatePostInput {
  title: String!
  content: String!
  authorId: ID!
}
```

### 2. **Resolvers**

```typescript
// ✅ Efficient resolvers with DataLoader
import DataLoader from 'dataloader';

const userLoader = new DataLoader(async (ids) => {
  const users = await db.users.findMany({
    where: { id: { in: ids } }
  });
  
  // Return in same order as input IDs
  return ids.map(id => users.find(u => u.id === id));
});

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      return await userLoader.load(id);
    },
    
    users: async (_, { limit = 20, offset = 0 }) => {
      return await db.users.findMany({
        take: limit,
        skip: offset
      });
    }
  },
  
  Mutation: {
    createUser: async (_, { input }) => {
      return await db.users.create(input);
    },
    
    updateUser: async (_, { id, input }) => {
      return await db.users.update(id, input);
    }
  },
  
  User: {
    // Resolver for User.posts field
    posts: async (user) => {
      return await db.posts.findMany({
        where: { authorId: user.id }
      });
    }
  }
};
```

### 3. **Error Handling**

```typescript
// ✅ GraphQL error handling
import { GraphQLError } from 'graphql';

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      const user = await db.users.findById(id);
      
      if (!user) {
        throw new GraphQLError('User not found', {
          extensions: {
            code: 'USER_NOT_FOUND',
            id
          }
        });
      }
      
      return user;
    }
  },
  
  Mutation: {
    createUser: async (_, { input }) => {
      try {
        return await db.users.create(input);
      } catch (error) {
        if (error.code === 'P2002') { // Unique constraint
          throw new GraphQLError('Email already exists', {
            extensions: {
              code: 'DUPLICATE_EMAIL',
              field: 'email'
            }
          });
        }
        throw error;
      }
    }
  }
};
```

## API Documentation

### 1. **OpenAPI/Swagger (REST)**

```typescript
// ✅ OpenAPI specification
/**
 * @swagger
 * /users:
 *   get:
 *     summary: List all users
 *     tags: [Users]
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *         description: Page number
 *     responses:
 *       200:
 *         description: Success
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/User'
 */
app.get('/users', async (req, res) => {
  // Implementation
});

// components/schemas/User
/**
 * @swagger
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       required:
 *         - id
 *         - email
 *       properties:
 *         id:
 *           type: string
 *           example: "123"
 *         name:
 *           type: string
 *           example: "John Doe"
 *         email:
 *           type: string
 *           format: email
 *           example: "john@example.com"
 */
```

### 2. **GraphQL Documentation**

```graphql
# ✅ Descriptions in schema (auto-documented)
"""
Represents a user in the system
"""
type User {
  """Unique identifier"""
  id: ID!
  
  """User's full name"""
  name: String!
  
  """User's email address"""
  email: String!
  
  """Posts authored by this user"""
  posts: [Post!]!
}

"""
Create a new user
"""
createUser(
  """User details"""
  input: CreateUserInput!
): User!
```

## API Best Practices

### 1. **Idempotency**

```typescript
// ✅ Idempotent operations (safe to retry)
app.put('/users/:id', async (req, res) => {
  // PUT is idempotent - same result on repeat
  const user = await db.users.upsert({
    where: { id: req.params.id },
    create: req.body,
    update: req.body
  });
  res.json(user);
});

// ✅ Idempotency keys for POST
app.post('/payments', async (req, res) => {
  const idempotencyKey = req.get('Idempotency-Key');
  
  if (!idempotencyKey) {
    return res.status(400).json({ error: 'Idempotency-Key required' });
  }
  
  // Check if already processed
  const existing = await cache.get(`payment:${idempotencyKey}`);
  if (existing) {
    return res.json(existing); // Return cached result
  }
  
  const payment = await processPayment(req.body);
  await cache.set(`payment:${idempotencyKey}`, payment, 24 * 60 * 60);
  
  res.status(201).json(payment);
});
```

### 2. **Caching**

```typescript
// ✅ HTTP caching headers
app.get('/products/:id', async (req, res) => {
  const product = await db.products.findById(req.params.id);
  
  // Generate ETag from content
  const etag = generateETag(product);
  
  // Check if client has current version
  if (req.get('If-None-Match') === etag) {
    return res.status(304).send(); // Not Modified
  }
  
  res.set({
    'ETag': etag,
    'Cache-Control': 'public, max-age=300', // 5 minutes
    'Last-Modified': product.updatedAt.toUTCString()
  });
  
  res.json(product);
});
```

### 3. **Webhooks**

```typescript
// ✅ Webhook system for async events
interface WebhookPayload {
  event: string;
  data: any;
  timestamp: string;
  signature: string; // HMAC for verification
}

async function sendWebhook(url: string, event: string, data: any) {
  const payload = {
    event,
    data,
    timestamp: new Date().toISOString()
  };
  
  // Sign payload
  const signature = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Webhook-Signature': signature
    },
    body: JSON.stringify({ ...payload, signature })
  });
}

// Trigger webhooks on events
app.post('/users', async (req, res) => {
  const user = await db.users.create(req.body);
  
  // Send webhook asynchronously
  sendWebhook(
    'https://customer.com/webhooks',
    'user.created',
    user
  ).catch(console.error);
  
  res.status(201).json(user);
});
```

## API Design Checklist

```
REST:
□ Resource-based URLs (nouns, not verbs)
□ Proper HTTP methods and status codes
□ Consistent response format
□ Pagination for lists
□ Filtering, sorting, field selection
□ API versioning strategy
□ Authentication (Bearer tokens, API keys)
□ Rate limiting configured
□ CORS properly configured
□ Error responses standardized

GraphQL:
□ Clear, typed schema
□ Efficient resolvers (DataLoader)
□ Pagination implemented (cursor or offset)
□ Error handling with codes
□ Authentication & authorization
□ Query complexity limits
□ Depth limiting
□ Introspection disabled in production

Documentation:
□ OpenAPI/GraphQL schema published
□ Example requests/responses
□ Authentication docs
□ Error codes documented
□ Changelog maintained
□ Migration guides for breaking changes

Performance:
□ Database queries optimized
□ N+1 queries eliminated
□ Response caching
□ Compression enabled (gzip)
□ CDN for static responses

Security:
□ Input validation on all endpoints
□ SQL injection prevention
□ Rate limiting per endpoint
□ API key rotation supported
□ Audit logging for sensitive operations
```

## Resources

- [REST API Tutorial](https://restfulapi.net/)
- [GraphQL Best Practices](https://graphql.org/learn/best-practices/)
- [OpenAPI Specification](https://swagger.io/specification/)
- [API Design Patterns](https://cloud.google.com/apis/design)

---

**Remember**: Great APIs are predictable, well-documented, and easy to use. Design for your API consumers, not just your implementation.

Attribution

Fabric-ProFabric-Pro
View sourceMore from Fabric-Pro →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

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 (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

284972 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

10311 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →