This commit is contained in:
2026-04-12 01:06:31 +07:00
commit 10d660cbcb
1066 changed files with 228596 additions and 0 deletions

View File

@@ -0,0 +1,495 @@
# Backend API Design
Comprehensive guide to designing RESTful, GraphQL, and gRPC APIs with best practices (2025).
## REST API Design
### Resource-Based URLs
**Good:**
```
GET /api/v1/users # List users
GET /api/v1/users/:id # Get specific user
POST /api/v1/users # Create user
PUT /api/v1/users/:id # Update user (full)
PATCH /api/v1/users/:id # Update user (partial)
DELETE /api/v1/users/:id # Delete user
GET /api/v1/users/:id/posts # Get user's posts
POST /api/v1/users/:id/posts # Create post for user
```
**Bad (Avoid):**
```
GET /api/v1/getUser?id=123 # RPC-style, not RESTful
POST /api/v1/createUser # Verb in URL
GET /api/v1/user-posts # Unclear relationship
```
### HTTP Status Codes (Meaningful Responses)
**Success:**
- `200 OK` - Successful GET, PUT, PATCH
- `201 Created` - Successful POST (resource created)
- `204 No Content` - Successful DELETE
**Client Errors:**
- `400 Bad Request` - Invalid input/validation error
- `401 Unauthorized` - Missing or invalid authentication
- `403 Forbidden` - Authenticated but not authorized
- `404 Not Found` - Resource doesn't exist
- `409 Conflict` - Resource conflict (duplicate email)
- `422 Unprocessable Entity` - Validation error (detailed)
- `429 Too Many Requests` - Rate limit exceeded
**Server Errors:**
- `500 Internal Server Error` - Generic server error
- `502 Bad Gateway` - Upstream service error
- `503 Service Unavailable` - Temporary downtime
- `504 Gateway Timeout` - Upstream service timeout
### Request/Response Format
**Request:**
```typescript
POST /api/v1/users
Content-Type: application/json
{
"email": "user@example.com",
"name": "John Doe",
"age": 30
}
```
**Success Response:**
```typescript
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/users/123
{
"id": "123",
"email": "user@example.com",
"name": "John Doe",
"age": 30,
"createdAt": "2025-01-09T12:00:00Z",
"updatedAt": "2025-01-09T12:00:00Z"
}
```
**Error Response:**
```typescript
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{
"field": "email",
"message": "Invalid email format",
"value": "invalid-email"
},
{
"field": "age",
"message": "Age must be between 18 and 120",
"value": 15
}
],
"timestamp": "2025-01-09T12:00:00Z",
"path": "/api/v1/users"
}
}
```
### Pagination
```typescript
// Request
GET /api/v1/users?page=2&limit=50
// Response
{
"data": [...],
"pagination": {
"page": 2,
"limit": 50,
"total": 1234,
"totalPages": 25,
"hasNext": true,
"hasPrev": true
},
"links": {
"first": "/api/v1/users?page=1&limit=50",
"prev": "/api/v1/users?page=1&limit=50",
"next": "/api/v1/users?page=3&limit=50",
"last": "/api/v1/users?page=25&limit=50"
}
}
```
### Filtering and Sorting
```
GET /api/v1/users?status=active&role=admin&sort=-createdAt,name&limit=20
# Filters: status=active AND role=admin
# Sort: createdAt DESC, name ASC
# Limit: 20 results
```
### API Versioning Strategies
**URL Versioning (Most Common):**
```
/api/v1/users
/api/v2/users
```
**Header Versioning:**
```
GET /api/users
Accept: application/vnd.myapi.v2+json
```
**Query Parameter:**
```
/api/users?version=2
```
**Recommendation:** URL versioning for simplicity and discoverability
## GraphQL API Design
### Schema Definition
```graphql
type User {
id: ID!
email: String!
name: String!
posts: [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
published: Boolean!
createdAt: DateTime!
}
type Query {
user(id: ID!): User
users(limit: Int = 50, offset: Int = 0): [User!]!
post(id: ID!): Post
posts(authorId: ID, published: Boolean): [Post!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
publishPost(id: ID!): Post!
}
input CreateUserInput {
email: String!
name: String!
password: String!
}
input UpdateUserInput {
email: String
name: String
}
```
### Queries
```graphql
# Flexible data fetching - client specifies exactly what they need
query {
user(id: "123") {
id
name
email
posts {
id
title
published
}
}
}
# With variables
query GetUser($userId: ID!) {
user(id: $userId) {
id
name
posts(published: true) {
title
}
}
}
```
### Mutations
```graphql
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
email
name
createdAt
}
}
# Variables
{
"input": {
"email": "user@example.com",
"name": "John Doe",
"password": "SecurePass123!"
}
}
```
### Resolvers (NestJS Example)
```typescript
@Resolver(() => User)
export class UserResolver {
constructor(
private userService: UserService,
private postService: PostService,
) {}
@Query(() => User, { nullable: true })
async user(@Args('id') id: string) {
return this.userService.findById(id);
}
@Query(() => [User])
async users(
@Args('limit', { defaultValue: 50 }) limit: number,
@Args('offset', { defaultValue: 0 }) offset: number,
) {
return this.userService.findAll({ limit, offset });
}
@Mutation(() => User)
async createUser(@Args('input') input: CreateUserInput) {
return this.userService.create(input);
}
// Field resolver - lazy load posts
@ResolveField(() => [Post])
async posts(@Parent() user: User) {
return this.postService.findByAuthorId(user.id);
}
}
```
### GraphQL Best Practices
1. **Avoid N+1 Problem** - Use DataLoader
```typescript
import DataLoader from 'dataloader';
const postLoader = new DataLoader(async (authorIds: string[]) => {
const posts = await db.posts.findAll({ where: { authorId: authorIds } });
return authorIds.map(id => posts.filter(p => p.authorId === id));
});
// In resolver
@ResolveField(() => [Post])
async posts(@Parent() user: User) {
return this.postLoader.load(user.id);
}
```
2. **Pagination** - Relay-style cursor pagination
3. **Error Handling** - Return errors in response
4. **Depth Limiting** - Prevent deeply nested queries
5. **Query Complexity Analysis** - Limit expensive queries
## gRPC API Design
### Protocol Buffers Schema
```protobuf
syntax = "proto3";
package user;
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
rpc CreateUser (CreateUserRequest) returns (User);
rpc UpdateUser (UpdateUserRequest) returns (User);
rpc DeleteUser (DeleteUserRequest) returns (DeleteUserResponse);
// Streaming
rpc StreamUsers (StreamUsersRequest) returns (stream User);
}
message User {
string id = 1;
string email = 2;
string name = 3;
int64 created_at = 4;
}
message GetUserRequest {
string id = 1;
}
message ListUsersRequest {
int32 limit = 1;
int32 offset = 2;
}
message ListUsersResponse {
repeated User users = 1;
int32 total = 2;
}
message CreateUserRequest {
string email = 1;
string name = 2;
string password = 3;
}
```
### Implementation (Node.js)
```typescript
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
const packageDefinition = protoLoader.loadSync('user.proto');
const userProto = grpc.loadPackageDefinition(packageDefinition).user;
// Server implementation
const server = new grpc.Server();
server.addService(userProto.UserService.service, {
async getUser(call, callback) {
const user = await userService.findById(call.request.id);
callback(null, user);
},
async createUser(call, callback) {
const user = await userService.create(call.request);
callback(null, user);
},
async streamUsers(call) {
const users = await userService.findAll();
for (const user of users) {
call.write(user);
}
call.end();
},
});
server.bindAsync(
'0.0.0.0:50051',
grpc.ServerCredentials.createInsecure(),
() => server.start()
);
```
### gRPC Benefits
- **Performance:** 7-10x faster than REST (binary protocol)
- **Streaming:** Bi-directional streaming
- **Type Safety:** Strong typing via Protocol Buffers
- **Code Generation:** Auto-generate client/server code
- **Best For:** Internal microservices, high-performance systems
## API Design Decision Matrix
| Feature | REST | GraphQL | gRPC |
|---------|------|---------|------|
| **Use Case** | Public APIs, CRUD | Flexible data fetching | Microservices, performance |
| **Performance** | Moderate | Moderate | Fastest (7-10x REST) |
| **Caching** | HTTP caching built-in | Complex | No built-in caching |
| **Browser Support** | Native | Native | Requires gRPC-Web |
| **Learning Curve** | Easy | Moderate | Steep |
| **Streaming** | Limited (SSE) | Subscriptions | Bi-directional |
| **Tooling** | Excellent | Excellent | Good |
| **Documentation** | OpenAPI/Swagger | Schema introspection | Protobuf definition |
## API Security Checklist
- [ ] HTTPS/TLS only (no HTTP)
- [ ] Authentication (OAuth 2.1, JWT, API keys)
- [ ] Authorization (RBAC, check permissions)
- [ ] Rate limiting (prevent abuse)
- [ ] Input validation (all endpoints)
- [ ] CORS configured properly
- [ ] Security headers (CSP, HSTS, X-Frame-Options)
- [ ] API versioning implemented
- [ ] Error messages don't leak system info
- [ ] Audit logging (who did what, when)
## API Documentation
### OpenAPI/Swagger (REST)
```yaml
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/api/v1/users:
get:
summary: List users
parameters:
- name: limit
in: query
schema:
type: integer
default: 50
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: string
email:
type: string
name:
type: string
```
## Resources
- **REST Best Practices:** https://restfulapi.net/
- **GraphQL:** https://graphql.org/learn/
- **gRPC:** https://grpc.io/docs/
- **OpenAPI:** https://swagger.io/specification/