Claude
Skills
Sign in
Back

backend-api-patterns

Included with Lifetime
$97 forever

Backend and API implementation patterns for scalability, security, and maintainability. Use when building APIs, services, and backend infrastructure.

Backend & APIs

What this skill does


This skill provides backend and API implementation patterns for building robust, scalable services.

## When to Invoke This Skill

Automatically activate for:
- API endpoint implementation
- Database operations and queries
- Authentication and authorization
- Caching and performance optimization
- Service architecture design

## API Design Patterns

### Consistent Response Structure

```typescript
// Standard API response envelope
interface ApiResponse<T> {
  data?: T;
  error?: {
    code: string;
    message: string;
    details?: Record<string, unknown>;
  };
  meta?: {
    pagination?: {
      page: number;
      pageSize: number;
      total: number;
      totalPages: number;
    };
    timestamp?: string;
    requestId?: string;
  };
}

// Success response helper
function success<T>(data: T, meta?: ApiResponse<T>['meta']): ApiResponse<T> {
  return { data, meta };
}

// Error response helper
function error(
  code: string,
  message: string,
  details?: Record<string, unknown>
): ApiResponse<never> {
  return { error: { code, message, details } };
}

// Paginated response helper
function paginated<T>(
  data: T[],
  page: number,
  pageSize: number,
  total: number
): ApiResponse<T[]> {
  return {
    data,
    meta: {
      pagination: {
        page,
        pageSize,
        total,
        totalPages: Math.ceil(total / pageSize),
      },
    },
  };
}
```

### Route Handler Pattern

```typescript
// Generic handler wrapper with error handling
type Handler<T> = (
  req: Request,
  context: { params: Record<string, string> }
) => Promise<T>;

function createHandler<T>(handler: Handler<T>) {
  return async (req: Request, context: { params: Record<string, string> }) => {
    const requestId = crypto.randomUUID();

    try {
      const result = await handler(req, context);
      return Response.json(success(result, { requestId }));
    } catch (err) {
      if (err instanceof AppError) {
        return Response.json(
          error(err.code, err.message),
          { status: err.statusCode }
        );
      }

      console.error(`[${requestId}] Unexpected error:`, err);
      return Response.json(
        error('INTERNAL_ERROR', 'An unexpected error occurred'),
        { status: 500 }
      );
    }
  };
}

// Usage
export const GET = createHandler(async (req, { params }) => {
  const user = await userService.findById(params.id);
  if (!user) throw new NotFoundError('User', params.id);
  return user;
});
```

## Service Layer Pattern

### Repository Pattern

```typescript
interface Repository<T, ID = string> {
  findById(id: ID): Promise<T | null>;
  findMany(options: FindOptions<T>): Promise<T[]>;
  count(filter?: Partial<T>): Promise<number>;
  create(data: CreateInput<T>): Promise<T>;
  update(id: ID, data: UpdateInput<T>): Promise<T>;
  delete(id: ID): Promise<void>;
}

interface FindOptions<T> {
  filter?: Partial<T>;
  orderBy?: keyof T;
  orderDir?: 'asc' | 'desc';
  limit?: number;
  offset?: number;
}

type CreateInput<T> = Omit<T, 'id' | 'createdAt' | 'updatedAt'>;
type UpdateInput<T> = Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>;

// Implementation
class UserRepository implements Repository<User> {
  constructor(private db: Database) {}

  async findById(id: string): Promise<User | null> {
    return this.db.query.users.findFirst({
      where: eq(users.id, id),
    });
  }

  async findMany(options: FindOptions<User>): Promise<User[]> {
    const { filter, orderBy, orderDir = 'asc', limit, offset } = options;

    return this.db.query.users.findMany({
      where: filter ? this.buildWhere(filter) : undefined,
      orderBy: orderBy ? (orderDir === 'asc' ? asc : desc)(users[orderBy]) : undefined,
      limit,
      offset,
    });
  }

  // ... other methods
}
```

### Service with Business Logic

```typescript
class UserService {
  constructor(
    private userRepo: Repository<User>,
    private cache: Cache,
    private eventBus: EventBus
  ) {}

  async getUser(id: string): Promise<User> {
    // Check cache first
    const cached = await this.cache.get<User>(`user:${id}`);
    if (cached) return cached;

    // Fetch from database
    const user = await this.userRepo.findById(id);
    if (!user) throw new NotFoundError('User', id);

    // Cache for future requests
    await this.cache.set(`user:${id}`, user, { ttl: 3600 });

    return user;
  }

  async createUser(input: CreateUserInput): Promise<User> {
    // Validate
    const existing = await this.userRepo.findMany({
      filter: { email: input.email },
      limit: 1,
    });
    if (existing.length > 0) {
      throw new ValidationError('Email already exists', { email: 'Already in use' });
    }

    // Hash password
    const hashedPassword = await hashPassword(input.password);

    // Create user
    const user = await this.userRepo.create({
      ...input,
      password: hashedPassword,
    });

    // Emit event for side effects
    await this.eventBus.emit('user.created', { userId: user.id });

    return user;
  }

  async updateUser(id: string, input: UpdateUserInput): Promise<User> {
    const user = await this.userRepo.update(id, input);

    // Invalidate cache
    await this.cache.delete(`user:${id}`);

    return user;
  }
}
```

## Authentication Patterns

### JWT with Refresh Tokens

```typescript
interface TokenPair {
  accessToken: string;   // Short-lived: 15 minutes
  refreshToken: string;  // Long-lived: 7 days
}

interface TokenPayload {
  sub: string;           // User ID
  email: string;
  roles: string[];
  type: 'access' | 'refresh';
}

class AuthService {
  constructor(
    private userRepo: Repository<User>,
    private tokenRepo: Repository<RefreshToken>,
    private jwtSecret: string
  ) {}

  async login(email: string, password: string): Promise<TokenPair> {
    const user = await this.userRepo.findMany({
      filter: { email },
      limit: 1,
    });

    if (!user[0] || !await verifyPassword(password, user[0].password)) {
      throw new UnauthorizedError('Invalid credentials');
    }

    return this.generateTokenPair(user[0]);
  }

  async refresh(refreshToken: string): Promise<TokenPair> {
    // Verify token
    const payload = this.verifyToken(refreshToken);
    if (payload.type !== 'refresh') {
      throw new UnauthorizedError('Invalid token type');
    }

    // Check if token is revoked
    const stored = await this.tokenRepo.findById(refreshToken);
    if (!stored || stored.revoked) {
      throw new UnauthorizedError('Token revoked');
    }

    // Get user and generate new tokens
    const user = await this.userRepo.findById(payload.sub);
    if (!user) throw new UnauthorizedError('User not found');

    // Revoke old refresh token
    await this.tokenRepo.update(refreshToken, { revoked: true });

    return this.generateTokenPair(user);
  }

  private generateTokenPair(user: User): TokenPair {
    const accessToken = jwt.sign(
      { sub: user.id, email: user.email, roles: user.roles, type: 'access' },
      this.jwtSecret,
      { expiresIn: '15m' }
    );

    const refreshToken = jwt.sign(
      { sub: user.id, type: 'refresh' },
      this.jwtSecret,
      { expiresIn: '7d' }
    );

    return { accessToken, refreshToken };
  }

  private verifyToken(token: string): TokenPayload {
    try {
      return jwt.verify(token, this.jwtSecret) as TokenPayload;
    } catch {
      throw new UnauthorizedError('Invalid or expired token');
    }
  }
}
```

### Middleware Pattern

```typescript
type Middleware = (req: Request, next: () => Promise<Response>) => Promise<Response>;

// Auth middleware
function authMiddleware(requiredRoles?: string[]): Middleware {
  return async (req, next) => {
    const token = req.headers.get('Authorization')?.replace('Bearer ', '');

    if (!token) {
      return Response.json(
        error('UNAUTHORIZED', 'No token provided'),
        { status: 401 }
      );
    }

    try {
      const payload = verifyToken(token);

      if (requiredRoles?.length && !requiredRoles.some(r => pay

Related in Backend & APIs