Building Irresistible APIs: A Personal REST Guidelines

Building Irresistible APIs: A Personal REST Guidelines

Elevate your API design from good to great. A breakdown of key REST principles focusing on consistency, strong resource modeling, and developer experience.

Building Irresistible APIs: A Personal REST Guidelines

The API-as-a-Product mindset is paramount to modern software development. An API is not just an interface; it’s a product that should be easy to understand, learn, and use. To achieve this, consistency is key. Following the footsteps of industry leaders, here is a personal interpretation of the core principles from RESTful API Guidelines, tailored for building robust and future-proof services.


Core Principle: API First and Resource Modeling

We MUST follow the API First principle: define the API contract before writing a single line of implementation code. This ensures a consistent, high-quality, and peer-reviewed design.

Naming and URL Conventions

URLs are the primary way clients interact with our system, so they must be clean and intuitive.

RuleDescriptionExample (GOOD)Example (BAD)
PluralizationMUST pluralize resource names./users/user
CaseMUST use kebab-case for path segments./order-items/orderItems
Verb-FreeMUST keep URLs verb-free, focusing on resources./users/{id}/addresses/get-all-users
Query ParamsMUST use snake_case for query parameters.?sort_by=created_at?sortBy=createdAt

Resource Relationship Diagram

Thinking in terms of resources and their relationships helps structure the URL hierarchy logically.

graph TD
    A[Client]
    subgraph API
        B[orders] -->|"GET (Collection)"| C(Order List);
        B -->|"POST (Create)"| D(New Order);
        B -->|"{order_id}"| E(Specific Order);
        E -->|"items"| F(Order Items);
        E -->|"items/{item_id}"| G(Specific Item);
    end
    A --> API

Payload and Data Consistency

A predictable data structure significantly improves the developer experience. We enforce strict conventions for JSON payloads.

JSON Payload Structure

We MUST use JSON as the payload data interchange format.

  1. Property Naming: MUST use snake_case for all JSON property names.
  2. Date/Time: MUST use standard formats like date-time (RFC 3339, e.g., 2017-04-12T23:20:50.52Z).
  3. Null vs. Absent: MUST use the same semantics for null and absent properties. Avoid null for boolean properties and empty arrays.

Example Payload

{
  "id": "ord-12345",
  "customer_id": "cust-9876",
  "status": "AWAITING_SHIPMENT",
  "total_amount": {
    "amount": 199.99,
    "currency": "EUR"
  },
  "created_at": "2025-11-10T10:00:00Z",
  "line_items": [
    {
      "sku": "product-a",
      "quantity": 1
    }
  ]
}

Idempotency and Request Handling

Idempotency is crucial for handling network errors and retries gracefully.

  • POST requests for creation are generally NOT idempotent.
  • SHOULD use a secondary key like an Idempotency-Key header for idempotent POSTs, allowing the client to safely retry.
  • PATCH requests (partial update) are often not inherently idempotent, but we SHOULD design them to be so when possible (e.g., using a state/version check like ETag).

Standardized HTTP Behavior

Using the HTTP specification correctly ensures our API behaves as expected by standard clients and proxies.

HTTP Methods and CRUD Mapping

We MUST use HTTP methods correctly based on their standard semantics:

HTTP MethodActionIdempotentExample Use
GETRetrieve a resource/collection.Yes/orders
POSTCreate a new resource.No (usually)/orders (body contains data)
PUTCompletely replace a resource.Yes/orders/{id} (body contains full resource)
PATCHPartially modify a resource.No (usually)/orders/{id} (body contains partial changes)
DELETERemove a resource.Yes/orders/{id}

Status Codes and Error Handling

We MUST use official and the most specific HTTP status codes available.

  • Success (2xx): 200 OK, 201 Created (for POST), 204 No Content (for DELETE).
  • Client Errors (4xx): 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found.
  • Rate Limiting: MUST use 429 Too Many Requests along with Retry-After header.

For error responses, we MUST support the Problem JSON specification (application/problem+json). This provides a consistent, machine-readable format for errors.

Example Problem JSON

{
  "type": "[https://example.com/probs/out-of-stock](https://example.com/probs/out-of-stock)",
  "title": "Not Enough Stock",
  "status": 409,
  "detail": "The requested quantity of item SKU-456 exceeds available stock.",
  "instance": "/orders/req-123"
}

Compatibility and Versioning Strategy

The goal is to avoid breaking changes and versioning for as long as possible. If a change is necessary, it SHOULD be done in a compatible way (e.g., adding a new optional field).

If an incompatible change is unavoidable, we MUST NOT use URL versioning (e.g., /v2/orders).

Instead, we MUST use Media Type Versioning. This is less coupled and allows for content negotiation.

Media Type Versioning Example

A client requests an incompatible version v2 of the cart resource:

GET /carts/{id} HTTP/1.1
Accept: application/x.personal-blog.cart+json;version=2

The server response will reflect the version it provides:

HTTP/1.1 200 OK
Content-Type: application/x.personal-blog.cart+json;version=2
Content-Length: ...

Summary

Building great APIs comes down to discipline and consistency. By adopting an API First approach, rigorously applying resource modeling principles (pluralization, kebab-case, verb-free URLs), and ensuring payload consistency (snake_case, standard formats), we lay the groundwork for a robust system. Furthermore, leveraging standard HTTP semantics (methods, specific status codes, Problem JSON) and employing media type versioning for incompatible changes ensures our API is both powerful and future-proof.


Resources

Related Posts