rest-api-design

安装量: 41
排名: #17546

安装

npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill rest-api-design

REST API Design Overview

Design REST APIs that are intuitive, consistent, and follow industry best practices for resource-oriented architecture.

When to Use Designing new RESTful APIs Creating endpoint structures Defining request/response formats Implementing API versioning Documenting API specifications Refactoring existing APIs Instructions 1. Resource Naming ✅ Good Resource Names (Nouns, Plural) GET /api/users GET /api/users/123 GET /api/users/123/orders POST /api/products DELETE /api/products/456

❌ Bad Resource Names (Verbs, Inconsistent) GET /api/getUsers POST /api/createProduct GET /api/user/123 (inconsistent singular/plural)

  1. HTTP Methods & Operations

CRUD Operations

GET /api/users # List all users (Read collection) GET /api/users/123 # Get specific user (Read single) POST /api/users # Create new user (Create) PUT /api/users/123 # Replace user completely (Update) PATCH /api/users/123 # Partial update user (Partial update) DELETE /api/users/123 # Delete user (Delete)

Nested Resources

GET /api/users/123/orders # Get user's orders POST /api/users/123/orders # Create order for user GET /api/users/123/orders/456 # Get specific order

  1. Request Examples Creating a Resource POST /api/users Content-Type: application/json

{ "email": "john@example.com", "firstName": "John", "lastName": "Doe", "role": "admin" }

Response: 201 Created Location: /api/users/789 { "id": "789", "email": "john@example.com", "firstName": "John", "lastName": "Doe", "role": "admin", "createdAt": "2025-01-15T10:30:00Z", "updatedAt": "2025-01-15T10:30:00Z" }

Updating a Resource PATCH /api/users/789 Content-Type: application/json

{ "firstName": "Jonathan" }

Response: 200 OK { "id": "789", "email": "john@example.com", "firstName": "Jonathan", "lastName": "Doe", "role": "admin", "updatedAt": "2025-01-15T11:00:00Z" }

  1. Query Parameters

Filtering

GET /api/products?category=electronics&inStock=true

Sorting

GET /api/users?sort=lastName,asc

Pagination

GET /api/users?page=2&limit=20

Field Selection

GET /api/users?fields=id,email,firstName

Search

GET /api/products?q=laptop

Multiple filters combined

GET /api/orders?status=pending&customer=123&sort=createdAt,desc&limit=50

  1. Response Formats Success Response { "data": { "id": "123", "email": "user@example.com", "firstName": "John" }, "meta": { "timestamp": "2025-01-15T10:30:00Z", "version": "1.0" } }

Collection Response with Pagination { "data": [ { "id": "1", "name": "Product 1" }, { "id": "2", "name": "Product 2" } ], "pagination": { "page": 2, "limit": 20, "total": 145, "totalPages": 8, "hasNext": true, "hasPrev": true }, "links": { "self": "/api/products?page=2&limit=20", "first": "/api/products?page=1&limit=20", "prev": "/api/products?page=1&limit=20", "next": "/api/products?page=3&limit=20", "last": "/api/products?page=8&limit=20" } }

Error Response { "error": { "code": "VALIDATION_ERROR", "message": "Invalid input data", "details": [ { "field": "email", "message": "Email format is invalid" }, { "field": "age", "message": "Must be at least 18" } ] }, "meta": { "timestamp": "2025-01-15T10:30:00Z", "requestId": "abc-123-def" } }

  1. HTTP Status Codes Success: 200 OK - Successful GET, PATCH, DELETE 201 Created - Successful POST (resource created) 204 No Content - Successful DELETE (no response body)

Client Errors: 400 Bad Request - Invalid request format/data 401 Unauthorized - Missing or invalid authentication 403 Forbidden - Authenticated but not authorized 404 Not Found - Resource doesn't exist 409 Conflict - Resource conflict (e.g., duplicate email) 422 Unprocessable - Validation errors 429 Too Many Requests - Rate limit exceeded

Server Errors: 500 Internal Server Error - Generic server error 503 Service Unavailable - Temporary unavailability

  1. API Versioning

URL Path Versioning (Recommended)

GET /api/v1/users GET /api/v2/users

Header Versioning

GET /api/users Accept: application/vnd.myapi.v1+json

Query Parameter (Not recommended)

GET /api/users?version=1

  1. Authentication & Security

JWT Bearer Token

GET /api/users Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

API Key

GET /api/users X-API-Key: your-api-key-here

Always use HTTPS in production

https://api.example.com/v1/users

  1. Rate Limiting Headers HTTP/1.1 200 OK X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 995 X-RateLimit-Reset: 1642262400

  2. OpenAPI Documentation openapi: 3.0.0 info: title: User API version: 1.0.0 description: User management API

paths: /users: get: summary: List all users parameters: - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 20 responses: '200': description: Successful response content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/User'

post:
  summary: Create a new user
  requestBody:
    required: true
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/UserInput'
  responses:
    '201':
      description: User created
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/User'
    '400':
      description: Invalid input
    '409':
      description: Email already exists

components: schemas: User: type: object properties: id: type: string email: type: string format: email firstName: type: string lastName: type: string createdAt: type: string format: date-time

UserInput:
  type: object
  required:
    - email
    - firstName
    - lastName
  properties:
    email:
      type: string
      format: email
    firstName:
      type: string
    lastName:
      type: string

Best Practices ✅ DO Use nouns for resources, not verbs Use plural names for collections Be consistent with naming conventions Return appropriate HTTP status codes Include pagination for collections Provide filtering and sorting options Version your API Document thoroughly with OpenAPI Use HTTPS Implement rate limiting Provide clear error messages Use ISO 8601 for dates ❌ DON'T Use verbs in endpoint names Return 200 for errors Expose internal IDs unnecessarily Over-nest resources (max 2 levels) Use inconsistent naming Forget authentication Return sensitive data Break backward compatibility without versioning Complete Example: Express.js const express = require('express'); const app = express();

app.use(express.json());

// List users with pagination app.get('/api/v1/users', async (req, res) => { try { const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 20; const offset = (page - 1) * limit;

const users = await User.findAndCountAll({
  limit,
  offset,
  attributes: ['id', 'email', 'firstName', 'lastName']
});

res.json({
  data: users.rows,
  pagination: {
    page,
    limit,
    total: users.count,
    totalPages: Math.ceil(users.count / limit)
  }
});

} catch (error) { res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'An error occurred while fetching users' } }); } });

// Get single user app.get('/api/v1/users/:id', async (req, res) => { try { const user = await User.findByPk(req.params.id);

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

res.json({ data: user });

} catch (error) { res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'An error occurred' } }); } });

// Create user app.post('/api/v1/users', async (req, res) => { try { const { email, firstName, lastName } = req.body;

// Validation
if (!email || !firstName || !lastName) {
  return res.status(400).json({
    error: {
      code: 'VALIDATION_ERROR',
      message: 'Missing required fields',
      details: [
        !email && { field: 'email', message: 'Email is required' },
        !firstName && { field: 'firstName', message: 'First name is required' },
        !lastName && { field: 'lastName', message: 'Last name is required' }
      ].filter(Boolean)
    }
  });
}

const user = await User.create({ email, firstName, lastName });

res.status(201)
   .location(`/api/v1/users/${user.id}`)
   .json({ data: user });

} catch (error) { if (error.name === 'SequelizeUniqueConstraintError') { return res.status(409).json({ error: { code: 'CONFLICT', message: 'Email already exists' } }); } res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'An error occurred' } }); } });

app.listen(3000);

返回排行榜