طراحی APIهای RESTful: بهترین شیوهها برای ساخت APIهای مقیاسپذیر
یک API که بهخوبی طراحی شده باشد، پایه و اساس هر اپلیکیشن موفقی است. این راهنما بهترین شیوههای طراحی APIهای RESTful را پوشش میدهد؛ APIهایی که شهودی، مقیاسپذیر و قابلنگهداری باشند.
قراردادهای نامگذاری منابع
از اسم استفاده کنید، نه فعل
# Good - Resources are nouns
GET /users # List users
POST /users # Create user
GET /users/123 # Get user
PUT /users/123 # Update user
DELETE /users/123 # Delete user
# Bad - Verbs in URLs
GET /getUsers
POST /createUser
GET /getUserById/123منابع سلسلهمراتبی
# Nested resources for relationships
GET /users/123/orders # User's orders
GET /users/123/orders/456 # Specific order
POST /users/123/orders # Create order for user
# Alternative: Query parameters for filtering
GET /orders?user_id=123 # Filter orders by user<Callout type="info"> برای مجموعهها از اسمهای جمع (/users) و برای منابع مشخص از شناسههای تکی (/users/123) استفاده کنید. </Callout>
نامگذاری یکدست
# Use kebab-case for multi-word resources
GET /user-profiles
GET /order-items
GET /shipping-addresses
# Use snake_case for query parameters
GET /products?category_id=5&sort_by=price&sort_order=descمتدهای HTTP و کدهای وضعیت
استفادهٔ درست از متدها
GET - Retrieve resources (idempotent, cacheable)
POST - Create new resources
PUT - Replace entire resource (idempotent)
PATCH - Partial update (not idempotent)
DELETE - Remove resource (idempotent)
OPTIONS - Get allowed methods
HEAD - Get headers onlyمرجع کدهای وضعیت
// Success responses
200 OK // Successful GET, PUT, PATCH
201 Created // Successful POST (include Location header)
204 No Content // Successful DELETE
// Client errors
400 Bad Request // Invalid request body/parameters
401 Unauthorized // Missing or invalid authentication
403 Forbidden // Authenticated but not authorized
404 Not Found // Resource doesn't exist
409 Conflict // Resource conflict (duplicate, etc.)
422 Unprocessable // Validation errors
429 Too Many Requests // Rate limit exceeded
// Server errors
500 Internal Error // Unexpected server error
502 Bad Gateway // Upstream service error
503 Service Unavailable // Temporary overloadطراحی درخواست و پاسخ
ساختار پاسخ یکدست
// Success response
{
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"meta": {
"request_id": "req_abc123"
}
}
// Collection response with pagination
{
"data": [
{ "id": 1, "name": "Product A" },
{ "id": 2, "name": "Product B" }
],
"meta": {
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8
},
"links": {
"self": "/products?page=1",
"next": "/products?page=2",
"last": "/products?page=8"
}
}
// Error response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "age",
"message": "Must be at least 18"
}
]
},
"meta": {
"request_id": "req_xyz789"
}
}<Note title="نکتهٔ API"> همیشه برای دیباگ و پشتیبانی، یک شناسهٔ درخواست (request ID) در پاسخها قرار دهید. </Note>
فیلتر کردن، مرتبسازی و صفحهبندی
# Filtering
GET /products?category=electronics&brand=apple&min_price=100&max_price=500
# Sorting
GET /products?sort=price:asc,created_at:desc
# Pagination (offset-based)
GET /products?page=2&per_page=20
# Pagination (cursor-based - better for large datasets)
GET /products?cursor=eyJpZCI6MTAwfQ&limit=20
# Field selection (sparse fieldsets)
GET /users/123?fields=id,name,email
# Including related resources
GET /orders/123?include=user,items,shipping_addressنسخهبندی API
نسخهبندی در مسیر URL (توصیهشده)
GET /v1/users
GET /v2/users
# Clear and explicit
# Easy to route and cache
# Visible in logs and documentationنسخهبندی با هدر
GET /users
Accept: application/vnd.myapi.v2+json
# Cleaner URLs
# More complex to implement
# Harder to test in browserاستراتژی منسوخسازی نسخهها
// Response headers for deprecated versions
{
"headers": {
"Deprecation": "Sun, 01 Jan 2025 00:00:00 GMT",
"Sunset": "Sun, 01 Jul 2025 00:00:00 GMT",
"Link": "</v2/users>; rel=\"successor-version\""
}
}احراز هویت و کنترل دسترسی
احراز هویت با JWT
// Request with Bearer token
GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// Token structure
{
"header": {
"alg": "RS256",
"typ": "JWT"
},
"payload": {
"sub": "user_123",
"email": "user@example.com",
"roles": ["user", "admin"],
"iat": 1704067200,
"exp": 1704153600
}
}احراز هویت با API Key
// Header-based (recommended)
GET /api/data
X-API-Key: sk_live_abc123xyz
// Query parameter (less secure - visible in logs)
GET /api/data?api_key=sk_live_abc123xyzاسکوپهای OAuth 2.0
# Define granular permissions
read:users - Read user data
write:users - Create/update users
delete:users - Delete users
read:orders - Read order data
admin - Full access
# Token with scopes
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:users read:orders"
}محدودیت نرخ درخواست (Rate Limiting)
هدرهای پیادهسازی
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1704067200
# When rate limited
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200استراتژیهای Rate Limit
// Sliding window implementation
const rateLimiter = {
// Per-user limits
authenticated: {
requests: 1000,
window: '1 hour'
},
// Per-IP limits for unauthenticated
anonymous: {
requests: 100,
window: '1 hour'
},
// Per-endpoint limits
endpoints: {
'POST /auth/login': { requests: 5, window: '1 minute' },
'POST /users': { requests: 10, window: '1 hour' }
}
};مدیریت خطا
پاسخ خطای جامع
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested user was not found",
"details": {
"resource_type": "User",
"resource_id": "123"
},
"documentation_url": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND",
"request_id": "req_abc123"
}
}کاتالوگ کدهای خطا
const errorCodes = {
// Authentication errors (1xxx)
INVALID_CREDENTIALS: { status: 401, message: 'Invalid email or password' },
TOKEN_EXPIRED: { status: 401, message: 'Authentication token has expired' },
INSUFFICIENT_PERMISSIONS: { status: 403, message: 'You do not have permission' },
// Validation errors (2xxx)
VALIDATION_ERROR: { status: 422, message: 'Request validation failed' },
INVALID_FORMAT: { status: 400, message: 'Invalid request format' },
// Resource errors (3xxx)
RESOURCE_NOT_FOUND: { status: 404, message: 'Resource not found' },
RESOURCE_CONFLICT: { status: 409, message: 'Resource already exists' },
// Rate limiting (4xxx)
RATE_LIMIT_EXCEEDED: { status: 429, message: 'Too many requests' },
// Server errors (5xxx)
INTERNAL_ERROR: { status: 500, message: 'An unexpected error occurred' },
SERVICE_UNAVAILABLE: { status: 503, message: 'Service temporarily unavailable' },
};HATEOAS و Hypermedia
پاسخهای خودتوصیفگر
{
"data": {
"id": 123,
"status": "pending",
"total": 99.99
},
"links": {
"self": { "href": "/orders/123" },
"customer": { "href": "/users/456" },
"items": { "href": "/orders/123/items" }
},
"actions": {
"cancel": {
"href": "/orders/123/cancel",
"method": "POST",
"title": "Cancel this order"
},
"pay": {
"href": "/orders/123/pay",
"method": "POST",
"title": "Process payment"
}
}
}مستندسازی API
مشخصات OpenAPI
openapi: 3.0.3
info:
title: My API
version: 1.0.0
description: A sample API
paths:
/users:
get:
summary: List all users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
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: integer
name:
type: string
email:
type: string
format: emailجمعبندی
APIهایی که خوب طراحی شده باشند، شهودی، یکدست و مقیاسپذیرند. با پیروی از این بهترین شیوهها — نامگذاری درست منابع، کدهای وضعیت مناسب، مدیریت خطای جامع و مستندسازی شفاف — APIهایی میسازید که توسعهدهندگان عاشق کار با آنها میشوند.
نکات کلیدی:
- برای منابع از اسم استفاده کنید و برای عملیات از متدهای HTTP
- کدهای وضعیت مناسب برگردانید
- نسخهبندی اصولی را از همان ابتدا پیادهسازی کنید
- پاسخهای خطای جامع طراحی کنید
- همهچیز را با OpenAPI مستند کنید
این یادداشت ترجمهٔ فارسی نوشتهٔ خودم است — نسخهٔ اصلی (انگلیسی) در dev.to