Claude
Skills
Sign in
Back

api-design

Included with Lifetime
$97 forever

# API Design Skill

Design

What this skill does

# API Design Skill

This skill allows Claude to automatically design and implement API endpoints following REST or GraphQL best practices when the context suggests an API needs to be created.

## When to Use This Skill

Claude should invoke this skill autonomously when:
- User mentions creating an API endpoint
- Discussion involves building backend routes
- Task requires data exposure via API
- User describes API functionality needed
- Code analysis shows an endpoint is referenced but doesn't exist

## What This Skill Does

Automatically generates a complete API endpoint structure including:
1. Route definition with proper HTTP methods
2. Controller/handler with business logic
3. Service layer for data operations
4. Request/response validation schemas
5. Authentication and authorization middleware
6. Error handling
7. API tests
8. OpenAPI/Swagger documentation

## Framework Detection

The skill detects the project's backend framework and generates appropriate code:
- **Express.js**: Route handlers, middleware
- **Fastify**: Route schemas, hooks
- **NestJS**: Controllers, modules, decorators
- **FastAPI**: Path operations, Pydantic models
- **Flask**: Blueprints, routes
- **Django**: Views, serializers

## Input Parameters

When invoked, the skill expects:
- `endpoint`: Route path (e.g., "/api/users", "/api/posts/:id")
- `method`: HTTP method (GET, POST, PUT, PATCH, DELETE)
- `description`: What the endpoint does
- `authentication`: Whether auth is required (boolean)
- `requestBody`: Request payload structure (if applicable)
- `responseBody`: Response structure

## Generated Structure

### For REST API (Node.js/Express):

```
src/
├── routes/
│   └── users.routes.ts          # Route definitions
├── controllers/
│   └── users.controller.ts      # Request handlers
├── services/
│   └── users.service.ts         # Business logic
├── validators/
│   └── users.validator.ts       # Input validation
├── types/
│   └── users.types.ts           # TypeScript types
├── middleware/
│   ├── auth.middleware.ts       # Authentication
│   └── validate.middleware.ts   # Validation
└── tests/
    └── users.test.ts            # API tests
```

## Component Templates

### 1. Route Definition

```typescript
// routes/users.routes.ts
import { Router } from 'express'
import { UsersController } from '../controllers/users.controller'
import { authenticate } from '../middleware/auth.middleware'
import { validate } from '../middleware/validate.middleware'
import { createUserSchema } from '../validators/users.validator'

const router = Router()
const controller = new UsersController()

router.get('/users', authenticate, controller.getUsers)
router.get('/users/:id', authenticate, controller.getUser)
router.post('/users', authenticate, validate(createUserSchema), controller.createUser)
router.put('/users/:id', authenticate, validate(updateUserSchema), controller.updateUser)
router.delete('/users/:id', authenticate, controller.deleteUser)

export default router
```

### 2. Controller

```typescript
// controllers/users.controller.ts
import { Request, Response, NextFunction } from 'express'
import { UsersService } from '../services/users.service'
import { CreateUserDto, UpdateUserDto } from '../types/users.types'

export class UsersController {
  private usersService: UsersService

  constructor() {
    this.usersService = new UsersService()
  }

  getUsers = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { page = 1, limit = 20, search } = req.query

      const result = await this.usersService.findAll({
        page: Number(page),
        limit: Number(limit),
        search: search as string,
      })

      res.json({
        success: true,
        data: result.data,
        pagination: result.pagination,
      })
    } catch (error) {
      next(error)
    }
  }

  getUser = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { id } = req.params
      const user = await this.usersService.findById(id)

      if (!user) {
        return res.status(404).json({
          success: false,
          error: { code: 'USER_NOT_FOUND', message: 'User not found' },
        })
      }

      res.json({
        success: true,
        data: user,
      })
    } catch (error) {
      next(error)
    }
  }

  createUser = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const userData: CreateUserDto = req.body
      const user = await this.usersService.create(userData)

      res.status(201).json({
        success: true,
        data: user,
      })
    } catch (error) {
      next(error)
    }
  }

  updateUser = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { id } = req.params
      const userData: UpdateUserDto = req.body

      const user = await this.usersService.update(id, userData)

      if (!user) {
        return res.status(404).json({
          success: false,
          error: { code: 'USER_NOT_FOUND', message: 'User not found' },
        })
      }

      res.json({
        success: true,
        data: user,
      })
    } catch (error) {
      next(error)
    }
  }

  deleteUser = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { id } = req.params
      await this.usersService.delete(id)

      res.status(204).send()
    } catch (error) {
      next(error)
    }
  }
}
```

### 3. Service Layer

```typescript
// services/users.service.ts
import { User } from '../entities/User'
import { CreateUserDto, UpdateUserDto } from '../types/users.types'
import { AppDataSource } from '../config/database'

export class UsersService {
  private userRepository = AppDataSource.getRepository(User)

  async findAll(options: { page: number; limit: number; search?: string }) {
    const { page, limit, search } = options
    const skip = (page - 1) * limit

    const queryBuilder = this.userRepository.createQueryBuilder('user')

    if (search) {
      queryBuilder.where('user.name ILIKE :search OR user.email ILIKE :search', {
        search: `%${search}%`,
      })
    }

    const [data, total] = await queryBuilder
      .skip(skip)
      .take(limit)
      .orderBy('user.createdAt', 'DESC')
      .getManyAndCount()

    return {
      data,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    }
  }

  async findById(id: string): Promise<User | null> {
    return this.userRepository.findOne({ where: { id } })
  }

  async create(data: CreateUserDto): Promise<User> {
    const user = this.userRepository.create(data)
    return this.userRepository.save(user)
  }

  async update(id: string, data: UpdateUserDto): Promise<User | null> {
    await this.userRepository.update(id, data)
    return this.findById(id)
  }

  async delete(id: string): Promise<void> {
    await this.userRepository.delete(id)
  }
}
```

### 4. Validation Schema

```typescript
// validators/users.validator.ts
import { z } from 'zod'

export const createUserSchema = z.object({
  body: z.object({
    email: z.string().email('Invalid email format'),
    name: z.string().min(2).max(100),
    password: z.string().min(8).max(100),
    role: z.enum(['user', 'admin']).optional(),
  }),
})

export const updateUserSchema = z.object({
  body: z.object({
    email: z.string().email().optional(),
    name: z.string().min(2).max(100).optional(),
    role: z.enum(['user', 'admin']).optional(),
  }),
})

export const getUsersSchema = z.object({
  query: z.object({
    page: z.string().regex(/^\d+$/).optional(),
    limit: z.string().regex(/^\d+$/).optional(),
    search: z.string().optional(),
  }),
})
```

### 5. Authentication Middleware

```typescript
// middleware/auth.middleware.ts
import { Request, Response, NextFunction } from 'express'
import jwt from 'jsonwebtoken'

export const authenticate = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const token = req.headers.authorization?.sp

Related in Design