home / skills / mastra-ai / mastra / api-design

This skill guides REST API and TypeScript interface design, enabling clean endpoints, consistent contracts, and scalable data modeling across services.

npx playbooks add skill mastra-ai/mastra --skill api-design

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.9 KB
---
name: api-design
description: Guidelines for designing RESTful APIs and TypeScript interfaces. Use when designing new endpoints, reviewing API contracts, or structuring data models.
license: MIT
---

# API Design Guidelines

## Overview

This skill provides guidelines for designing clean, consistent APIs. Apply these patterns when creating new endpoints, defining TypeScript interfaces, or reviewing API contracts.

**Keywords**: API design, REST, endpoints, TypeScript interfaces, data modeling, HTTP methods, response formats

## REST Endpoint Design

### URL Structure

```
/{resource}                    # Collection
/{resource}/{id}               # Single resource
/{resource}/{id}/{sub-resource} # Nested resource
```

**Good examples:**

- `GET /users` - List users
- `GET /users/123` - Get user 123
- `POST /users/123/orders` - Create order for user 123

**Avoid:**

- `/getUsers` - Don't use verbs in URLs
- `/user/list` - Don't use action words
- `/users/123/getOrders` - HTTP method implies action

### HTTP Methods

| Method | Purpose          | Idempotent | Body |
| ------ | ---------------- | ---------- | ---- |
| GET    | Read resource    | Yes        | No   |
| POST   | Create resource  | No         | Yes  |
| PUT    | Replace resource | Yes        | Yes  |
| PATCH  | Update resource  | No         | Yes  |
| DELETE | Remove resource  | Yes        | No   |

### Response Codes

**Success:**

- `200` - OK (GET, PUT, PATCH with body)
- `201` - Created (POST)
- `204` - No Content (DELETE, PUT/PATCH without body)

**Client errors:**

- `400` - Bad Request (validation failed)
- `401` - Unauthorized (no/invalid auth)
- `403` - Forbidden (no permission)
- `404` - Not Found
- `409` - Conflict (duplicate, state conflict)
- `422` - Unprocessable Entity (semantic errors)

**Server errors:**

- `500` - Internal Server Error
- `503` - Service Unavailable

## Response Formats

### Success Response

```typescript
// Single resource
{
  "data": {
    "id": "123",
    "name": "Example",
    "createdAt": "2024-01-15T10:30:00Z"
  }
}

// Collection
{
  "data": [...],
  "meta": {
    "total": 100,
    "page": 1,
    "pageSize": 20
  }
}
```

### Error Response

```typescript
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format"
      }
    ]
  }
}
```

## TypeScript Interface Design

### Naming Conventions

```typescript
// Entities use PascalCase nouns
interface User {}
interface OrderItem {}

// Request/Response types include suffix
interface CreateUserRequest {}
interface GetUserResponse {}

// Params types for function arguments
interface ListUsersParams {}
```

### Required vs Optional

```typescript
// Required fields: always present
interface User {
  id: string;
  email: string;
  createdAt: Date;
}

// Optional fields: may be undefined
interface CreateUserRequest {
  email: string;
  name?: string; // Optional
  metadata?: Record<string, unknown>;
}
```

### Discriminated Unions

Use for type-safe variants:

```typescript
type PaymentMethod =
  | { type: 'card'; cardNumber: string; expiry: string }
  | { type: 'bank'; accountNumber: string; routingNumber: string }
  | { type: 'crypto'; walletAddress: string };

// Usage
function processPayment(method: PaymentMethod) {
  switch (method.type) {
    case 'card':
      // TypeScript knows cardNumber exists
      return chargeCard(method.cardNumber);
    case 'bank':
      return initiateTransfer(method.accountNumber);
    case 'crypto':
      return sendCrypto(method.walletAddress);
  }
}
```

## Pagination

### Offset-based (Simple)

```typescript
interface PaginationParams {
  page?: number; // Default: 1
  pageSize?: number; // Default: 20, max: 100
}

interface PaginatedResponse<T> {
  data: T[];
  meta: {
    page: number;
    pageSize: number;
    total: number;
    totalPages: number;
  };
}
```

### Cursor-based (Scalable)

```typescript
interface CursorParams {
  cursor?: string;
  limit?: number;
}

interface CursorResponse<T> {
  data: T[];
  meta: {
    nextCursor: string | null;
    hasMore: boolean;
  };
}
```

## Versioning

### URL Versioning (Recommended)

```
/api/v1/users
/api/v2/users
```

### Header Versioning

```
Accept: application/vnd.api+json; version=2
```

## Common Patterns

### Filtering

```
GET /users?status=active&role=admin
GET /orders?createdAfter=2024-01-01&createdBefore=2024-02-01
```

### Sorting

```
GET /users?sort=createdAt:desc
GET /users?sort=lastName:asc,firstName:asc
```

### Field Selection

```
GET /users?fields=id,name,email
GET /users/123?include=orders,profile
```

## Security Considerations

1. **Authentication** - Use Bearer tokens in Authorization header
2. **Rate limiting** - Include `X-RateLimit-*` headers
3. **Input validation** - Validate all request bodies with schemas
4. **Output encoding** - Escape HTML in responses if rendered
5. **CORS** - Configure allowed origins explicitly

Overview

This skill provides practical guidelines for designing RESTful APIs and TypeScript interfaces. It helps teams create consistent endpoints, clear response shapes, and type-safe data models. Use it to standardize contracts, improve client-server interoperability, and reduce review friction.

How this skill works

The skill describes URL structure, HTTP method semantics, standard response codes, and payload formats for success and error responses. It outlines TypeScript naming conventions, required vs optional fields, discriminated unions, and pagination strategies (offset and cursor). It also covers versioning, filtering/sorting/field selection patterns, and essential security practices.

When to use it

  • Designing new REST endpoints or evolving existing APIs
  • Defining TypeScript request/response interfaces and domain entities
  • Conducting API contract reviews or pull request audits
  • Choosing a pagination strategy for a list endpoint
  • Standardizing error formats and HTTP status usage

Best practices

  • Use resource-based URLs; avoid verbs or action words in paths
  • Map HTTP methods to intents: GET read, POST create, PUT replace, PATCH update, DELETE remove
  • Return consistent success envelopes (data + meta) and structured error objects with codes and details
  • Prefer URL versioning for breaking changes; use header versioning sparingly
  • Model variants with discriminated unions for exhaustive type-safe handling
  • Validate inputs with schemas, apply rate limiting, and use Bearer auth and explicit CORS rules

Example use cases

  • Create a users API: GET /api/v1/users (paginated), GET /api/v1/users/:id, POST /api/v1/users
  • Design a typed request/response pair: CreateUserRequest and GetUserResponse with required and optional fields
  • Implement cursor-based feeds for scalable lists like activity streams
  • Standardize error handling across services using an {error:{code,message,details}} shape
  • Add filtering and sorting to list endpoints: GET /orders?status=paid&sort=createdAt:desc

FAQ

When should I use cursor vs offset pagination?

Use offset pagination for simple UIs and small datasets. Choose cursor pagination for large or frequently changing datasets where stable, performant paging is required.

Should I include action names in URLs?

No. Keep URLs resource-focused and use HTTP methods to express actions. Avoid verbs like /getUsers or /user/list; prefer /users and the appropriate method.