Source profileQuality 80/100

affaan-m/ECC/docs/es/skills/api-design/SKILL.md

api-design

Patrones de diseño REST API incluyendo nomenclatura de recursos, códigos de estado, paginación, filtrado, respuestas de error, versionado y rate limiting para APIs de producción.

Source repository stars
234,327
Declared platforms
1
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Convenciones y buenas prácticas para diseñar APIs REST consistentes y amigables para desarrolladores.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorDeclaredSource recordInstall path and trigger
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/es/skills/api-design"
    Safe inspection promptEditorial

    Inspect the Agent Skill "api-design" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/es/skills/api-design/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Cuándo Activar

      Diseñar nuevos endpoints de API

      Diseñar nuevos endpoints de APIRevisar contratos de API existentesAgregar paginación, filtrado u ordenamiento
    2. 02

      Diseño de Recursos

      Review the “Diseño de Recursos” section in the pinned source before continuing.

      Review and apply the “Diseño de Recursos” source section.
    3. 03

      Estructura de URL

      Review the “Estructura de URL” section in the pinned source before continuing.

      Review and apply the “Estructura de URL” source section.
    4. 04

      Los recursos son sustantivos, plural, minúsculas, kebab-case

      GET /api/v1/users GET /api/v1/users/:id POST /api/v1/users PUT /api/v1/users/:id PATCH /api/v1/users/:id DELETE /api/v1/users/:id

      GET /api/v1/users GET /api/v1/users/:id POST /api/v1/users PUT /api/v1/users/:id PATCH /api/v1/users/:id DELETE /api/v1/users/:id
    5. 05

      Sub-recursos para relaciones

      GET /api/v1/users/:id/orders POST /api/v1/users/:id/orders

      GET /api/v1/users/:id/orders POST /api/v1/users/:id/orders

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score80/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility1 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/es/skills/api-design/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Patrones de Diseño de API

    Convenciones y buenas prácticas para diseñar APIs REST consistentes y amigables para desarrolladores.

    Cuándo Activar

    • Diseñar nuevos endpoints de API
    • Revisar contratos de API existentes
    • Agregar paginación, filtrado u ordenamiento
    • Implementar manejo de errores para APIs
    • Planificar la estrategia de versionado de API
    • Construir APIs públicas o para partners

    Diseño de Recursos

    Estructura de URL

    # Los recursos son sustantivos, plural, minúsculas, kebab-case
    GET    /api/v1/users
    GET    /api/v1/users/:id
    POST   /api/v1/users
    PUT    /api/v1/users/:id
    PATCH  /api/v1/users/:id
    DELETE /api/v1/users/:id
    
    # Sub-recursos para relaciones
    GET    /api/v1/users/:id/orders
    POST   /api/v1/users/:id/orders
    
    # Acciones que no mapean a CRUD (usar verbos con moderación)
    POST   /api/v1/orders/:id/cancel
    POST   /api/v1/auth/login
    POST   /api/v1/auth/refresh
    

    Reglas de Nomenclatura

    # BIEN
    /api/v1/team-members          # kebab-case para recursos de varias palabras
    /api/v1/orders?status=active  # query params para filtrado
    /api/v1/users/123/orders      # recursos anidados para pertenencia
    
    # MAL
    /api/v1/getUsers              # verbo en la URL
    /api/v1/user                  # singular (usar plural)
    /api/v1/team_members          # snake_case en URLs
    /api/v1/users/123/getOrders   # verbo en recurso anidado
    

    Métodos HTTP y Códigos de Estado

    Semántica de Métodos

    MétodoIdempotenteSeguroUsar Para
    GETRecuperar recursos
    POSTNoNoCrear recursos, disparar acciones
    PUTNoReemplazo completo de un recurso
    PATCHNo*NoActualización parcial de un recurso
    DELETENoEliminar un recurso

    *PATCH puede hacerse idempotente con la implementación adecuada

    Referencia de Códigos de Estado

    # Éxito
    200 OK                    — GET, PUT, PATCH (con cuerpo de respuesta)
    201 Created               — POST (incluir header Location)
    204 No Content            — DELETE, PUT (sin cuerpo de respuesta)
    
    # Errores de Cliente
    400 Bad Request           — Fallo de validación, JSON malformado
    401 Unauthorized          — Autenticación ausente o inválida
    403 Forbidden             — Autenticado pero no autorizado
    404 Not Found             — El recurso no existe
    409 Conflict              — Entrada duplicada, conflicto de estado
    422 Unprocessable Entity  — Semánticamente inválido (JSON válido, datos incorrectos)
    429 Too Many Requests     — Límite de rate excedido
    
    # Errores de Servidor
    500 Internal Server Error — Fallo inesperado (nunca exponer detalles)
    502 Bad Gateway           — Falló el servicio upstream
    503 Service Unavailable   — Sobrecarga temporal, incluir Retry-After
    

    Errores Comunes

    # MAL: 200 para todo
    { "status": 200, "success": false, "error": "Not found" }
    
    # BIEN: Usar códigos de estado HTTP semánticamente
    HTTP/1.1 404 Not Found
    { "error": { "code": "not_found", "message": "User not found" } }
    
    # MAL: 500 para errores de validación
    # BIEN: 400 o 422 con detalles por campo
    
    # MAL: 200 para recursos creados
    # BIEN: 201 con header Location
    HTTP/1.1 201 Created
    Location: /api/v1/users/abc-123
    

    Formato de Respuesta

    Respuesta Exitosa

    {
      "data": {
        "id": "abc-123",
        "email": "alice@example.com",
        "name": "Alice",
        "created_at": "2025-01-15T10:30:00Z"
      }
    }
    

    Respuesta de Colección (con Paginación)

    {
      "data": [
        { "id": "abc-123", "name": "Alice" },
        { "id": "def-456", "name": "Bob" }
      ],
      "meta": {
        "total": 142,
        "page": 1,
        "per_page": 20,
        "total_pages": 8
      },
      "links": {
        "self": "/api/v1/users?page=1&per_page=20",
        "next": "/api/v1/users?page=2&per_page=20",
        "last": "/api/v1/users?page=8&per_page=20"
      }
    }
    

    Respuesta de Error

    {
      "error": {
        "code": "validation_error",
        "message": "Request validation failed",
        "details": [
          {
            "field": "email",
            "message": "Must be a valid email address",
            "code": "invalid_format"
          },
          {
            "field": "age",
            "message": "Must be between 0 and 150",
            "code": "out_of_range"
          }
        ]
      }
    }
    

    Variantes de Envelope de Respuesta

    // Opción A: Envelope con wrapper data (recomendado para APIs públicas)
    interface ApiResponse<T> {
      data: T;
      meta?: PaginationMeta;
      links?: PaginationLinks;
    }
    
    interface ApiError {
      error: {
        code: string;
        message: string;
        details?: FieldError[];
      };
    }
    
    // Opción B: Respuesta plana (más simple, común para APIs internas)
    // Éxito: retornar el recurso directamente
    // Error: retornar objeto de error
    // Distinguir por código de estado HTTP
    

    Paginación

    Basada en Offset (Simple)

    GET /api/v1/users?page=2&per_page=20
    
    # Implementación
    SELECT * FROM users
    ORDER BY created_at DESC
    LIMIT 20 OFFSET 20;
    

    Pros: Fácil de implementar, soporta "saltar a página N" Contras: Lento en offsets grandes (OFFSET 100000), inconsistente con inserciones concurrentes

    Basada en Cursor (Escalable)

    GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20
    
    # Implementación
    SELECT * FROM users
    WHERE id > :cursor_id
    ORDER BY id ASC
    LIMIT 21;  -- obtener uno extra para determinar has_next
    
    {
      "data": [...],
      "meta": {
        "has_next": true,
        "next_cursor": "eyJpZCI6MTQzfQ"
      }
    }
    

    Pros: Rendimiento consistente independientemente de la posición, estable con inserciones concurrentes Contras: No se puede saltar a una página arbitraria, el cursor es opaco

    Cuándo Usar Cuál

    Caso de UsoTipo de Paginación
    Dashboards administrativos, datasets pequeños (<10K)Offset
    Scroll infinito, feeds, datasets grandesCursor
    APIs públicasCursor (por defecto) con offset (opcional)
    Resultados de búsquedaOffset (los usuarios esperan números de página)

    Filtrado, Ordenamiento y Búsqueda

    Filtrado

    # Igualdad simple
    GET /api/v1/orders?status=active&customer_id=abc-123
    
    # Operadores de comparación (usar notación de corchetes)
    GET /api/v1/products?price[gte]=10&price[lte]=100
    GET /api/v1/orders?created_at[after]=2025-01-01
    
    # Múltiples valores (separados por coma)
    GET /api/v1/products?category=electronics,clothing
    
    # Campos anidados (notación de punto)
    GET /api/v1/orders?customer.country=US
    

    Ordenamiento

    # Campo único (prefijo - para descendente)
    GET /api/v1/products?sort=-created_at
    
    # Múltiples campos (separados por coma)
    GET /api/v1/products?sort=-featured,price,-created_at
    

    Búsqueda de Texto Completo

    # Parámetro de consulta de búsqueda
    GET /api/v1/products?q=wireless+headphones
    
    # Búsqueda específica de campo
    GET /api/v1/users?email=alice
    

    Conjuntos de Campos Reducidos (Sparse Fieldsets)

    # Retornar solo los campos especificados (reduce el payload)
    GET /api/v1/users?fields=id,name,email
    GET /api/v1/orders?fields=id,total,status&include=customer.name
    

    Autenticación y Autorización

    Autenticación Basada en Token

    # Bearer token en el header Authorization
    GET /api/v1/users
    Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
    
    # API key (para servidor a servidor)
    GET /api/v1/data
    X-API-Key: sk_live_abc123
    

    Patrones de Autorización

    // A nivel de recurso: verificar propiedad
    app.get("/api/v1/orders/:id", async (req, res) => {
      const order = await Order.findById(req.params.id);
      if (!order) return res.status(404).json({ error: { code: "not_found" } });
      if (order.userId !== req.user.id) return res.status(403).json({ error: { code: "forbidden" } });
      return res.json({ data: order });
    });
    
    // Basada en roles: verificar permisos
    app.delete("/api/v1/users/:id", requireRole("admin"), async (req, res) => {
      await User.delete(req.params.id);
      return res.status(204).send();
    });
    

    Rate Limiting

    Headers

    HTTP/1.1 200 OK
    X-RateLimit-Limit: 100
    X-RateLimit-Remaining: 95
    X-RateLimit-Reset: 1640000000
    
    # Cuando se excede
    HTTP/1.1 429 Too Many Requests
    Retry-After: 60
    {
      "error": {
        "code": "rate_limit_exceeded",
        "message": "Rate limit exceeded. Try again in 60 seconds."
      }
    }
    

    Niveles de Rate Limit

    NivelLímiteVentanaCaso de Uso
    Anónimo30/minPor IPEndpoints públicos
    Autenticado100/minPor usuarioAcceso API estándar
    Premium1000/minPor API keyPlanes de API de pago
    Interno10000/minPor servicioServicio a servicio

    Versionado

    Versionado en Ruta de URL (Recomendado)

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

    Pros: Explícito, fácil de enrutar, cacheable Contras: La URL cambia entre versiones

    Versionado por Header

    GET /api/users
    Accept: application/vnd.myapp.v2+json
    

    Pros: URLs limpias Contras: Más difícil de probar, fácil de olvidar

    Estrategia de Versionado

    1. Empezar con /api/v1/ — no versionar hasta que sea necesario
    2. Mantener como máximo 2 versiones activas (actual + anterior)
    3. Línea de tiempo de deprecación:
       - Anunciar la deprecación (6 meses de aviso para APIs públicas)
       - Agregar header Sunset: Sunset: Sat, 01 Jan 2026 00:00:00 GMT
       - Retornar 410 Gone después de la fecha de sunset
    4. Los cambios no disruptivos no necesitan una nueva versión:
       - Agregar nuevos campos a las respuestas
       - Agregar nuevos parámetros de consulta opcionales
       - Agregar nuevos endpoints
    5. Los cambios disruptivos requieren una nueva versión:
       - Eliminar o renombrar campos
       - Cambiar tipos de campo
       - Cambiar la estructura de URL
       - Cambiar el método de autenticación
    

    Patrones de Implementación

    TypeScript (Next.js API Route)

    import { z } from "zod";
    import { NextRequest, NextResponse } from "next/server";
    
    const createUserSchema = z.object({
      email: z.string().email(),
      name: z.string().min(1).max(100),
    });
    
    export async function POST(req: NextRequest) {
      const body = await req.json();
      const parsed = createUserSchema.safeParse(body);
    
      if (!parsed.success) {
        return NextResponse.json({
          error: {
            code: "validation_error",
            message: "Request validation failed",
            details: parsed.error.issues.map(i => ({
              field: i.path.join("."),
              message: i.message,
              code: i.code,
            })),
          },
        }, { status: 422 });
      }
    
      const user = await createUser(parsed.data);
    
      return NextResponse.json(
        { data: user },
        {
          status: 201,
          headers: { Location: `/api/v1/users/${user.id}` },
        },
      );
    }
    

    Python (Django REST Framework)

    from rest_framework import serializers, viewsets, status
    from rest_framework.response import Response
    
    class CreateUserSerializer(serializers.Serializer):
        email = serializers.EmailField()
        name = serializers.CharField(max_length=100)
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = User
            fields = ["id", "email", "name", "created_at"]
    
    class UserViewSet(viewsets.ModelViewSet):
        serializer_class = UserSerializer
        permission_classes = [IsAuthenticated]
    
        def get_serializer_class(self):
            if self.action == "create":
                return CreateUserSerializer
            return UserSerializer
    
        def create(self, request):
            serializer = CreateUserSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = UserService.create(**serializer.validated_data)
            return Response(
                {"data": UserSerializer(user).data},
                status=status.HTTP_201_CREATED,
                headers={"Location": f"/api/v1/users/{user.id}"},
            )
    

    Go (net/http)

    func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
        var req CreateUserRequest
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
            writeError(w, http.StatusBadRequest, "invalid_json", "Invalid request body")
            return
        }
    
        if err := req.Validate(); err != nil {
            writeError(w, http.StatusUnprocessableEntity, "validation_error", err.Error())
            return
        }
    
        user, err := h.service.Create(r.Context(), req)
        if err != nil {
            switch {
            case errors.Is(err, domain.ErrEmailTaken):
                writeError(w, http.StatusConflict, "email_taken", "Email already registered")
            default:
                writeError(w, http.StatusInternalServerError, "internal_error", "Internal error")
            }
            return
        }
    
        w.Header().Set("Location", fmt.Sprintf("/api/v1/users/%s", user.ID))
        writeJSON(w, http.StatusCreated, map[string]any{"data": user})
    }
    

    Lista de Verificación de Diseño de API

    Antes de publicar un nuevo endpoint:

    • La URL del recurso sigue las convenciones de nomenclatura (plural, kebab-case, sin verbos)
    • Se usa el método HTTP correcto (GET para lecturas, POST para creaciones, etc.)
    • Se retornan códigos de estado apropiados (no 200 para todo)
    • La entrada se valida con esquema (Zod, Pydantic, Bean Validation)
    • Las respuestas de error siguen el formato estándar con códigos y mensajes
    • Se implementa paginación para endpoints de lista (cursor u offset)
    • Autenticación requerida (o marcado explícitamente como público)
    • Autorización verificada (el usuario solo puede acceder a sus propios recursos)
    • Rate limiting configurado
    • La respuesta no filtra detalles internos (stack traces, errores SQL)
    • Nomenclatura consistente con los endpoints existentes (camelCase vs snake_case)
    • Documentado (especificación OpenAPI/Swagger actualizada)

    Alternatives

    Compare before choosing