DevToolBoxGRATIS
Blog

REST API Mejores Prácticas: La guía completa para 2026

15 min de lecturapor DevToolBox

Construir una API REST bien diseñada es una de las habilidades más importantes para los desarrolladores modernos. Una gran API es intuitiva, consistente, segura y fácil de mantener. Esta guía completa cubre todo, desde convenciones de nomenclatura de URL y métodos HTTP hasta autenticación, paginación, limitación de velocidad y errores comunes.

Convenciones de nomenclatura de URL

La base de una buena API REST comienza con URLs bien diseñadas. Tus endpoints deben ser predecibles, legibles y seguir patrones consistentes.

Usa sustantivos, no verbos

Los recursos REST deben identificarse mediante sustantivos. El método HTTP ya indica la acción.

# Good - nouns as resources
GET    /users          # Get all users
GET    /users/123      # Get user 123
POST   /users          # Create a new user
DELETE /users/123      # Delete user 123

# Bad - verbs in URLs
GET    /getUsers
POST   /createUser
DELETE /deleteUser/123

Usa plurales para colecciones

Siempre usa sustantivos en plural para endpoints de colección. Esto mantiene la consistencia.

# Good - consistent plurals
GET /users          # Collection
GET /users/123      # Single resource in collection
GET /products       # Collection
GET /products/456   # Single resource

# Bad - inconsistent or singular
GET /user           # Ambiguous: one user or all users?
GET /user/123
GET /product-list

Recursos anidados para relaciones

Usa el anidamiento para expresar relaciones entre recursos, pero evita ir más allá de dos niveles.

# Good - clear parent-child relationship (max 2 levels)
GET /users/123/orders          # Orders belonging to user 123
GET /users/123/orders/456      # Specific order of user 123

# Bad - too deeply nested
GET /users/123/orders/456/items/789/reviews
# Better: use a direct resource endpoint
GET /order-items/789/reviews
# Or: flatten with query parameters
GET /reviews?order_item_id=789

Patrones de URL: Buenos vs Malos

BuenoMaloRazón
GET /usersGET /getUsersHTTP method already implies the action
GET /users/123GET /user?id=123Use path parameters for resource identity
POST /usersPOST /createUserPOST already means create
PUT /users/123POST /updateUserPUT/PATCH for updates, use resource path
DELETE /users/123POST /deleteUserUse DELETE method, not POST with verb
GET /users/123/ordersGET /getUserOrders?userId=123Use nested resources for relationships
GET /products?category=electronicsGET /electronics/productsUse query params for filtering
GET /users?status=active&sort=nameGET /active-users-sorted-by-nameUse query params, not encoded URLs
PATCH /users/123POST /users/123/updateUse HTTP methods, not URL verbs
GET /users/123/avatarGET /getAvatarForUser/123Keep it resource-oriented

Métodos HTTP

Cada método HTTP tiene semántica específica. Usarlos correctamente hace tu API predecible.

MétodoPropósitoIdempotenteCuerpo de solicitudEjemplo
GETRetrieve a resource or collectionYesNoGET /users/123
POSTCreate a new resourceNoYesPOST /users
PUTReplace a resource entirelyYesYesPUT /users/123
PATCHPartially update a resourceNo*YesPATCH /users/123
DELETERemove a resourceYesOptionalDELETE /users/123

* PATCH can be idempotent depending on implementation. If the patch describes the final state (e.g., {"name": "John"}), it is idempotent. If it describes an operation (e.g., {"op": "increment", "path": "/count"}), it is not.

Códigos de estado HTTP

Usar los códigos de estado correctos ayuda a los clientes a manejar las respuestas apropiadamente.

CódigoNombreCuándo usar
200OKSuccessful GET, PUT, PATCH, or DELETE. Return the resource or confirmation.
201CreatedSuccessful POST that created a new resource. Include Location header with the new resource URL.
204No ContentSuccessful DELETE or PUT/PATCH when no response body is needed.
400Bad RequestThe request is malformed — invalid JSON, missing required fields, wrong data types.
401UnauthorizedNo valid authentication credentials provided. The client must authenticate first.
403ForbiddenThe client is authenticated but does not have permission for this action.
404Not FoundThe requested resource does not exist. Also use for hidden resources (instead of 403) for security.
409ConflictThe request conflicts with the current state — duplicate email, version mismatch, etc.
422Unprocessable EntityThe request is well-formed but fails validation — email format invalid, password too short.
429Too Many RequestsRate limit exceeded. Include Retry-After header with seconds until the client can retry.
500Internal Server ErrorAn unexpected server error occurred. Log the details server-side but do not expose them to clients.
503Service UnavailableThe server is temporarily unavailable — maintenance mode, overloaded. Include Retry-After header.

Manejo de errores

Las respuestas de error consistentes hacen tu API más fácil de consumir. Cada error debe devolver un cuerpo JSON estructurado.

Formato estándar de respuesta de error

Usa un sobre de error consistente para todas las respuestas de error.

// Standard error response
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "The user with ID 123 was not found.",
    "status": 404,
    "timestamp": "2026-01-15T10:30:00Z",
    "request_id": "req_abc123def456",
    "documentation_url": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND"
  }
}

// Another example: authentication error
{
  "error": {
    "code": "TOKEN_EXPIRED",
    "message": "The access token has expired. Please refresh your token.",
    "status": 401,
    "timestamp": "2026-01-15T10:30:00Z",
    "request_id": "req_xyz789"
  }
}

Errores de validación a nivel de campo

Para errores de validación 422, incluye información de error a nivel de campo.

// 422 Validation error with field-level details
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The request body contains invalid fields.",
    "status": 422,
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address.",
        "rejected_value": "not-an-email"
      },
      {
        "field": "password",
        "message": "Must be at least 8 characters long.",
        "rejected_value": "short"
      },
      {
        "field": "age",
        "message": "Must be a positive integer.",
        "rejected_value": -5
      }
    ],
    "timestamp": "2026-01-15T10:30:00Z",
    "request_id": "req_val456"
  }
}

Autenticación y autorización

Elegir la estrategia de autenticación correcta depende de tu caso de uso.

Claves API

Tokens de cadena simples pasados en encabezados o parámetros de consulta. Ideales para comunicación servidor a servidor.

# API Key in header (recommended)
GET /api/users HTTP/1.1
Host: api.example.com
X-API-Key: sk_live_abc123def456ghi789

# API Key in query parameter (less secure - visible in logs)
GET /api/users?api_key=sk_live_abc123def456ghi789

Tokens JWT Bearer

Tokens autocontenidos que llevan reclamaciones del usuario. Ideales para autenticación sin estado en microservicios.

# JWT Bearer Token in Authorization header
GET /api/users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

# Token refresh flow
POST /api/auth/refresh HTTP/1.1
Content-Type: application/json
{
  "refresh_token": "rt_abc123def456"
}

# Response
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_new789xyz"
}

OAuth 2.0

El estándar de la industria para autorización delegada. Permite a aplicaciones de terceros acceder a recursos en nombre de un usuario.

# OAuth 2.0 Authorization Code Flow

# Step 1: Redirect user to authorization server
GET https://auth.example.com/authorize?
  response_type=code&
  client_id=your_client_id&
  redirect_uri=https://yourapp.com/callback&
  scope=read:users write:users&
  state=random_csrf_token

# Step 2: Exchange authorization code for tokens
POST https://auth.example.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTH_CODE_FROM_CALLBACK&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://yourapp.com/callback

# Step 3: Use the access token
GET /api/users/me HTTP/1.1
Authorization: Bearer ACCESS_TOKEN_HERE

Comparación de autenticación

CaracterísticaAPI KeyJWTOAuth 2.0
ComplexityLowMediumHigh
StatelessYesYesDepends
User contextNoYesYes
Token expirationManual rotationBuilt-in (exp claim)Built-in
Scope/permissionsLimitedVia claimsFine-grained scopes
Third-party accessNoNoYes (delegated)
Best forServer-to-serverSPAs, Mobile appsThird-party integrations
RevocationDelete keyBlacklist neededRevoke at auth server

Patrones de paginación

Cualquier endpoint que devuelva una lista de recursos debe soportar paginación.

Paginación basada en offset

El enfoque más simple. Usa número de página y parámetros de límite.

# Offset-based pagination request
GET /api/users?page=2&limit=20

# How it works internally
SELECT * FROM users
ORDER BY created_at DESC
LIMIT 20 OFFSET 20;  -- Skip first 20, return next 20

# Pros: Simple, allows jumping to any page
# Cons: Inconsistent with concurrent modifications,
#        slow on large offsets (OFFSET 100000)

Paginación basada en cursor

Usa un token cursor opaco para marcar la posición en el conjunto de datos.

# Cursor-based pagination request
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20

# The cursor is an opaque token (often base64-encoded)
# that points to a specific position in the dataset

# How it works internally (cursor decodes to {"id": 123})
SELECT * FROM users
WHERE id > 123
ORDER BY id ASC
LIMIT 20;

# Pros: Consistent results, fast on large datasets
# Cons: Cannot jump to arbitrary pages,
#        only next/previous navigation

Formato de respuesta de paginación

Incluye metadatos sobre el estado de paginación en cada respuesta paginada.

// Offset-based pagination response
{
  "data": [
    { "id": 21, "name": "Alice" },
    { "id": 22, "name": "Bob" }
    // ... 18 more items
  ],
  "meta": {
    "current_page": 2,
    "per_page": 20,
    "total_pages": 15,
    "total_count": 294
  },
  "links": {
    "self": "/api/users?page=2&limit=20",
    "first": "/api/users?page=1&limit=20",
    "prev": "/api/users?page=1&limit=20",
    "next": "/api/users?page=3&limit=20",
    "last": "/api/users?page=15&limit=20"
  }
}

// Cursor-based pagination response
{
  "data": [
    { "id": 124, "name": "Charlie" },
    { "id": 125, "name": "Diana" }
    // ... 18 more items
  ],
  "meta": {
    "has_more": true,
    "next_cursor": "eyJpZCI6MTQzfQ",
    "prev_cursor": "eyJpZCI6MTI0fQ"
  },
  "links": {
    "next": "/api/users?cursor=eyJpZCI6MTQzfQ&limit=20",
    "prev": "/api/users?cursor=eyJpZCI6MTI0fQ&limit=20&direction=prev"
  }
}

Estrategias de versionado de API

Las APIs evolucionan con el tiempo. El versionado te permite hacer cambios incompatibles sin interrumpir los clientes existentes.

EstrategiaEjemploVentajasDesventajas
URL Path/v1/users, /v2/usersExplicit, easy to route and cache, clear in logs and docsURL pollution, harder to sunset old versions
Custom HeaderAccept-Version: v2 or X-API-Version: 2Clean URLs, version hidden from casual usersHarder to test (need tools to set headers), not cacheable by default
Query Parameter/users?version=2Easy to test in browser, no URL path changesCan be accidentally omitted, harder to route, pollutes query string
Content NegotiationAccept: application/vnd.api.v2+jsonMost RESTful approach, per-resource versioningComplex, hard to test, poorly supported by many tools
# URL Path versioning (recommended)
GET /v1/users/123        # Version 1
GET /v2/users/123        # Version 2 with breaking changes

# Express.js router example
const v1Router = express.Router();
const v2Router = express.Router();

v1Router.get('/users/:id', v1UserController.get);
v2Router.get('/users/:id', v2UserController.get);

app.use('/v1', v1Router);
app.use('/v2', v2Router);

Limitación de velocidad

La limitación de velocidad protege tu API del abuso y asegura un uso justo.

Encabezados de limitación de velocidad

Incluye estos encabezados en cada respuesta para que los clientes puedan monitorear su uso.

HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000          # Max requests per window
X-RateLimit-Remaining: 847       # Remaining requests in window
X-RateLimit-Reset: 1706176800    # Unix timestamp when window resets
X-RateLimit-Window: 3600         # Window duration in seconds (1 hour)

# Some APIs also include:
RateLimit-Policy: 1000;w=3600    # IETF draft standard format

Respuesta 429 Demasiadas solicitudes

Cuando un cliente excede el límite, devuelve un estado 429 con encabezado Retry-After.

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 45
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706176800

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "You have exceeded the rate limit of 1000 requests per hour.",
    "status": 429,
    "retry_after": 45,
    "documentation_url": "https://api.example.com/docs/rate-limiting"
  }
}

Estrategias de backoff

Los clientes deben implementar backoff exponencial cuando reciben respuestas 429.

// Exponential backoff with jitter
async function fetchWithRetry(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    // Use Retry-After header if available
    const retryAfter = response.headers.get('Retry-After');
    let delay;

    if (retryAfter) {
      delay = parseInt(retryAfter, 10) * 1000;
    } else {
      // Exponential backoff: 1s, 2s, 4s, 8s, 16s
      const baseDelay = Math.pow(2, attempt) * 1000;
      // Add jitter (random 0-1s) to prevent thundering herd
      const jitter = Math.random() * 1000;
      delay = baseDelay + jitter;
    }

    console.log(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1})`);
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error('Max retries exceeded');
}

Encabezados de seguridad

Los encabezados de seguridad protegen tu API de vulnerabilidades web comunes.

EncabezadoValor recomendadoPropósito
Access-Control-Allow-Originhttps://yourapp.com (specific origin)CORS: Controls which domains can call your API. Never use * in production with credentials.
Access-Control-Allow-MethodsGET, POST, PUT, PATCH, DELETECORS: Specifies which HTTP methods are allowed for cross-origin requests.
Access-Control-Allow-HeadersAuthorization, Content-TypeCORS: Specifies which request headers are allowed in cross-origin requests.
Strict-Transport-Securitymax-age=31536000; includeSubDomainsHSTS: Forces browsers to use HTTPS for all future requests to your domain.
Content-Security-Policydefault-src 'none'; frame-ancestors 'none'CSP: Prevents XSS and data injection attacks. For APIs, restrict everything.
X-Content-Type-OptionsnosniffPrevents browsers from MIME-type sniffing. Ensures responses are treated as their declared content type.
X-Frame-OptionsDENYPrevents your API responses from being embedded in iframes (clickjacking protection).
Cache-Controlno-store, no-cache, must-revalidatePrevents caching of sensitive API responses. Adjust per endpoint as needed.
// Express.js security headers middleware
const helmet = require('helmet');

app.use(helmet());

// Custom CORS configuration
const cors = require('cors');
app.use(cors({
  origin: ['https://yourapp.com', 'https://admin.yourapp.com'],
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Authorization', 'Content-Type'],
  credentials: true,
  maxAge: 86400  // Cache preflight for 24 hours
}));

// Additional security headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('Cache-Control', 'no-store');
  next();
});

Documentación API con OpenAPI

Una buena documentación es esencial para la adopción de la API. OpenAPI es el estándar para describir APIs REST.

  • Escribe primero la especificación OpenAPI (enfoque design-first), luego implementa la API.
  • Incluye descripciones detalladas para cada endpoint, parámetro y esquema de respuesta.
  • Proporciona valores de ejemplo realistas para todos los cuerpos de solicitud y respuesta.
  • Documenta las respuestas de error para cada endpoint.
  • Usa etiquetas para agrupar endpoints relacionados.
  • Versiona tu especificación OpenAPI junto con tu código API.
  • Configura documentación interactiva (Swagger UI o Redoc).
# OpenAPI 3.1 specification example
openapi: 3.1.0
info:
  title: User Management API
  version: 1.0.0
  description: API for managing users and their resources

servers:
  - url: https://api.example.com/v1

paths:
  /users:
    get:
      summary: List all users
      tags: [Users]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: A paginated list of users
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
              example:
                data:
                  - id: 1
                    name: "Alice Johnson"
                    email: "alice@example.com"
                meta:
                  current_page: 1
                  total_pages: 10
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      summary: Create a new user
      tags: [Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
            example:
              name: "Bob Smith"
              email: "bob@example.com"
              password: "securePassword123"
      responses:
        '201':
          description: User created successfully
        '422':
          $ref: '#/components/responses/ValidationError'

Errores comunes de REST API

Incluso los desarrolladores experimentados cometen estos errores. Revisa esta lista para asegurarte de que tu API sigue las mejores prácticas.

#ErrorPor qué está malSolución
1Using verbs in URLsREST is resource-oriented; HTTP methods express actionsUse nouns: /users not /getUsers
2Returning 200 for errorsClients rely on status codes for error handlingUse proper status codes: 400, 404, 422, 500
3Not paginating list endpointsReturns grow unbounded, causing timeouts and memory issuesAlways paginate with sensible defaults (limit=20)
4Exposing internal errorsStack traces reveal implementation details to attackersLog details server-side, return generic error messages
5Ignoring CORS headersBrowser-based clients cannot call your APIConfigure CORS for allowed origins and methods
6Not versioning the APIBreaking changes affect all clients simultaneouslyUse URL path versioning: /v1/users
7Using POST for everythingLoses HTTP method semantics, breaks caching and idempotencyUse GET, POST, PUT, PATCH, DELETE appropriately
8Inconsistent naming conventionsMixed case or formats confuse API consumersPick one (snake_case or camelCase) and be consistent
9No rate limitingAPI is vulnerable to abuse and DDoS attacksImplement rate limiting with clear headers
10Missing Content-Type headerClients cannot parse responses correctlyAlways set Content-Type: application/json

Prueba estas herramientas de desarrollo relacionadas

Preguntas frecuentes

¿Cuál es la diferencia entre REST y RESTful?

REST es un estilo arquitectónico definido por Roy Fielding. RESTful es un adjetivo que describe APIs que adhieren a las restricciones REST. En la práctica, los términos se usan indistintamente.

¿Debo usar PUT o PATCH para actualizar recursos?

Usa PUT cuando el cliente envía un reemplazo completo del recurso. Usa PATCH para actualizaciones parciales. PATCH es más eficiente en ancho de banda.

¿Cómo debo manejar el versionado de API?

El versionado por ruta URL (ej: /v1/users) es el enfoque más común y explícito. Es fácil de entender, enrutar y cachear.

¿Cuál es la mejor forma de manejar la autenticación en APIs REST?

Para la mayoría de aplicaciones, los tokens JWT Bearer ofrecen un buen equilibrio entre seguridad y simplicidad. Usa tokens de acceso de corta duración con rotación de tokens de refresco.

¿Cómo diseño recursos anidados en APIs REST?

Usa el anidamiento para mostrar relaciones padre-hijo: /users/123/orders. Limita el anidamiento a dos niveles máximo.

¿Las respuestas de API REST deben incluir campos null u omitirlos?

Depende de tu contrato. Incluir campos null hace el esquema predecible. Omitirlos reduce el tamaño del payload. Sé consistente y documéntalo.

𝕏 Twitterin LinkedIn
¿Fue útil?

Mantente actualizado

Recibe consejos de desarrollo y nuevas herramientas.

Sin spam. Cancela cuando quieras.

Prueba estas herramientas relacionadas

4xxHTTP Status Code Reference>>cURL to Code ConverterJWTJWT Decoder{ }JSON Formatter

Artículos relacionados

Códigos de estado HTTP: Guía de referencia completa para desarrolladores

Referencia completa de códigos HTTP: 1xx a 5xx con explicaciones prácticas, mejores prácticas API y consejos de depuración.

Autenticación API: OAuth 2.0 vs JWT vs API Key

Compara métodos de autenticación API: OAuth 2.0, tokens JWT Bearer y claves API. Cuándo usar cada método.