Nội dung

Dai Phan

Dai Phan

Full-Stack Developer

Full-stack developer passionate about modern web technologies, best practices, and sharing knowledge with the community.

Skills & Expertise

JavaScript TypeScript React Node.js DevOps
150+
Articles
50k+
Readers
4.9
Rating

API Design Best Practices

Những nguyên tắc vàng trong thiết kế API RESTful và GraphQL

API Design Best Practices

Thiết kế API tốt là nền tảng cho việc xây dựng các ứng dụng scalable và maintainable. Dưới đây là những best practices quan trọng cần tuân thủ.

RESTful API Principles

1. Use Proper HTTP Methods

GET    /api/users        # Get all users
GET    /api/users/123    # Get specific user
POST   /api/users        # Create new user
PUT    /api/users/123    # Update entire user
PATCH  /api/users/123    # Partial update
DELETE /api/users/123    # Delete user

2. Consistent Naming Conventions

  • Use plural nouns for resources: /users, /products
  • Use kebab-case for multi-word resources: /user-profiles
  • Be consistent across all endpoints

3. Proper Status Codes

// Success codes
200 OK          - Request succeeded
201 Created     - Resource created successfully
204 No Content  - Request succeeded, no content to return

// Error codes
400 Bad Request      - Invalid request data
401 Unauthorized     - Authentication required
403 Forbidden        - Insufficient permissions
404 Not Found        - Resource not found
409 Conflict         - Resource conflict
500 Internal Error   - Server error

Response Format Best Practices

1. Consistent JSON Structure

{
  "success": true,
  "data": {
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com"
  },
  "message": "User retrieved successfully",
  "timestamp": "2024-01-20T10:30:00Z"
}

2. Error Response Format

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email format",
    "details": {
      "field": "email",
      "value": "invalid-email"
    }
  },
  "timestamp": "2024-01-20T10:30:00Z"
}

Authentication & Authorization

1. JWT Token Implementation

// Generate token
const token = jwt.sign(
  {
    userId: user.id,
    email: user.email
  },
  process.env.JWT_SECRET,
  { expiresIn: '24h' }
);

// Authorization header format
Authorization: Bearer <jwt_token>

2. API Key Security

// Validate API key
const validateApiKey = (req, res, next) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey || !isValidApiKey(apiKey)) {
    return res.status(401).json({
      success: false,
      error: { code: 'INVALID_API_KEY', message: 'Invalid API key' }
    });
  }
  next();
};

Rate Limiting & Caching

1. Implement Rate Limiting

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
  message: {
    success: false,
    error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests' }
  }
});

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

2. Cache Strategy

// Cache GET requests
app.get('/api/users', cache('5 minutes'), async (req, res) => {
  const users = await User.find();
  res.json({
    success: true,
    data: users,
    count: users.length
  });
});

Versioning Strategy

1. URL Versioning

GET /api/v1/users
GET /api/v2/users  # Updated version with breaking changes

2. Header Versioning

GET /api/users
Accept: application/vnd.api+json;version=1

Documentation Standards

1. OpenAPI/Swagger Specification

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
  description: API for managing users

paths:
  /users:
    get:
      summary: Get all users
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'

2. Interactive Documentation

const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

Security Best Practices

1. Input Validation

const Joi = require('joi');

const userSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/).required()
});

app.post('/api/users', (req, res) => {
  const { error } = userSchema.validate(req.body);
  if (error) {
    return res.status(400).json({
      success: false,
      error: { code: 'VALIDATION_ERROR', message: error.details[0].message }
    });
  }
  // Process request
});

2. CORS Configuration

const cors = require('cors');

app.use(cors({
  origin: ['https://yourdomain.com', 'https://admin.yourdomain.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
}));

Monitoring & Logging

1. Request Logging

const morgan = require('morgan');

app.use(morgan('combined', {
  stream: {
    write: (message) => {
      logger.info(message.trim());
    }
  }
}));

2. Health Check Endpoint

app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage()
  });
});

GraphQL Considerations

1. Schema Design

type User {
  id: ID!
  name: String!
  email: String!
  createdAt: DateTime!
}

type Query {
  users(limit: Int, offset: Int): [User!]!
  user(id: ID!): User
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
}

2. Resolvers Best Practices

const resolvers = {
  Query: {
    users: async (_, { limit = 10, offset = 0 }, { db }) => {
      return await db.user.findMany({
        take: limit,
        skip: offset,
        orderBy: { createdAt: 'desc' }
      });
    }
  }
};

Performance Optimization

1. Database Query Optimization

// Bad: N+1 query problem
const users = await User.find();
for (const user of users) {
  user.posts = await Post.find({ userId: user.id });
}

// Good: Use eager loading
const users = await User.find().populate('posts');

2. Pagination Implementation

app.get('/api/users', async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const skip = (page - 1) * limit;

  const [users, total] = await Promise.all([
    User.find().skip(skip).limit(limit),
    User.countDocuments()
  ]);

  res.json({
    success: true,
    data: users,
    pagination: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit)
    }
  });
});

Testing Strategies

1. Unit Tests

describe('User API', () => {
  test('should create a new user', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        name: 'John Doe',
        email: 'john@example.com',
        password: 'Password123'
      })
      .expect(201);

    expect(response.body.success).toBe(true);
    expect(response.body.data.name).toBe('John Doe');
  });
});

2. Integration Tests

test('should handle user authentication flow', async () => {
  // Register user
  await request(app)
    .post('/api/users/register')
    .send(userData);

  // Login
  const loginResponse = await request(app)
    .post('/api/users/login')
    .send({ email: userData.email, password: userData.password });

  // Access protected route
  await request(app)
    .get('/api/users/profile')
    .set('Authorization', `Bearer ${loginResponse.body.data.token}`)
    .expect(200);
});

Kết luận

Thiết kế API tốt không chỉ làm cho application dễ sử dụng mà còn đảm bảo scalability, security, và maintainability. Bằng cách tuân thủ các best practices trên, bạn có thể xây dựng APIs chất lượng cao và professional.