# Delete order 123
curl -X DELETE https://api.example.com/orders/123
```

### Level 3: Hypermedia controls (HATEOAS)

HATEOAS distinguishes Level 3 APIs from the rest. At Level 3, responses include hypermedia links that offer clients dynamic navigation paths within the API. This allows for discoverability directly embedded in API responses, fostering a dynamic interaction model in which clients can follow links to escalate through states or access related resources.

## Evaluating your API's RESTfulness

To evaluate the RESTfulness of your API, consider the following questions:

1. Level 1: Are resources uniquely identified by URIs?

   For example, `/orders/123` uniquely identifies an order resource.

2. Level 2: Are all URLs resource-oriented, and do they use standard HTTP methods?

   For example, URLs should use GET to retrieve resources, POST to create new resources, PUT to update existing resources, and DELETE to remove resources.

   None of your endpoint URLs should contain verbs or actions. For example, neither `/cancelOrder` nor `/orders/123/cancel` is resource-oriented,.

3. Level 3: Do responses include hypermedia links for dynamic navigation?

   For example, a response might include links to related resources, allowing clients to navigate the API without prior knowledge of its structure.

## The RESTful sweet spot

We believe the RESTful sweet spot lies somewhere between Level 1 and Level 2 of the Richardson Maturity Model. This is where most APIs can find a balance between adhering to RESTful principles and practicality. 

In case it wasn't crystal clear from the previous sections, we think HATEOAS isn't practical or relevant for most APIs. It was a powerful concept when the web was young, and APIs were meant to be consumed by humans.

Here's what we recommend:

1. **Embrace resource-oriented design**: Think in terms of resources rather than actions. Identify the key resources in your API domain and structure your endpoints around them.

   This ensures your API is predictable and intuitive, making it easier for developers to understand and use.

   ✅ Good: Use resource-oriented URLs like `/orders` and `/customers`.

   ```yaml filename="openapi.yaml"
   paths:
     /orders: # Resource-oriented
       get:
         summary: Retrieve a list of orders
       post:
         summary: Create a new order
     /orders/{orderId}: # Resource-oriented
       get:
         summary: Retrieve order details
       patch:
         summary: Update an order
       put:
         summary: Replace an order
       delete:
         summary: Delete an order
   ```

   ❌ Bad: Don't use action-based URLs like `/cancelOrder`.

   ```yaml filename="openapi.yaml"
   paths:
     /cancelOrder: # Not resource-oriented
       post:
         summary: Cancel an order
   ```

   ✅ Compromise: Use sub-resources like `/orders/{orderId}/cancellations`.

   If you must include actions in your URLs, consider using sub-resources to represent them as resources in their own right.

   ```yaml filename="openapi.yaml"
   paths:
     /orders/{orderId}/cancellations: # Resource-oriented
       post:
         summary: Create a cancellation for an order
   ```

   ✅ Less ideal compromise: Use top-level actions like `/orders/{orderId}/cancel`.

   ```yaml filename="openapi.yaml"
   paths:
     /orders/{orderId}/cancel: # Resource-oriented with action
       post:
         summary: Cancel an order
   ```

2. **Use standard HTTP methods wisely**: Choose the appropriate method for each operation - GET for retrieval, POST for creation, PATCH for partial updates, PUT for complete updates or replacement, and DELETE for removal. Ensure your API follows the semantics of these methods.

   This allows developers to predict how operations will behave based on the HTTP method. It also implies idempotency, safety, and cacheability where applicable.

   An operation is considered **safe** if it doesn't modify resources. It is **idempotent** if the result of performing it multiple times is the same as performing it once. It is **cacheable** if the response can be stored and reused.

   ```yaml filename="openapi.yaml"
   paths:
     /orders:
       get: # Safe, idempotent, cacheable
         summary: Retrieve a list of orders
       post: # Unsafe, potentially idempotent, not cacheable
         summary: Create a new order
     /orders/{orderId}:
       get: # safe, idempotent, cacheable
         summary: Retrieve order details
       patch: # unsafe, potentially idempotent, not cacheable
         summary: Update an order
       put: # unsafe, idempotent, not cacheable
         summary: Replace an order
       delete: # unsafe, idempotent, not cacheable
         summary: Delete an order
   ```

3. **Document thoroughly with OpenAPI**: Instead of relying on HATEOAS for dynamic navigation, use OpenAPI to provide comprehensive documentation for your API. OpenAPI allows you to define your API's structure, endpoints, methods, parameters, and responses in a machine-readable format. This ensures clarity and type safety for developers using your API.

### How targeting SDK generation enables better API design

While you're designing your API, consider how it will be consumed by developers. If you're providing an SDK or client library, you can optimize your API design to make SDK generation easier and more effective, and you may find the optimized design also leads to a more RESTful API.

We often see APIs with action-based URLs like `/orders/{orderId}/cancel` or `/orders/{orderId}/refund`. While partially resource-oriented, these URLs include actions as part of the URL.

Firstly, these URLs are not as maintainable as resource-oriented URLs. If you decide to allow multiple cancellations for an order - for example, when an order is restored after being canceled and then canceled again - you may wish to represent cancellations as resources in their own right. This would lead to URLs like `/orders/{orderId}/cancellations/{cancellationId}`. Alternatively, you may wish to allow partial refunds, leading to URLs like `/orders/{orderId}/refunds/{refundId}`.

Secondly, these URLs are not as predictable as resource-oriented URLs. Developers may not know which actions are available for a given resource, leading to a reliance on documentation or trial and error.

From a design perspective, this could look like the following:

```yaml filename="openapi.yaml"
paths:
  /orders/{orderId}/cancellations:
    post:
      summary: Set the order status to cancelled
  /orders/{orderId}/refunds:
    post:
      summary: Create a refund for the order
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                amount:
                  type: number
                  minimum: 0
                  maximum: 100
```

Then, in a future version of your API, you could introduce cancellations and refunds as resources in their own right:

```yaml filename="openapi.yaml"
paths:
  /orders/{orderId}/cancellations:
    get:
      summary: Retrieve a list of cancellations for the order
    post:
      summary: Create a new cancellation for the order
  /orders/{orderId}/cancellations/{cancellationId}:
    get:
      summary: Retrieve a specific cancellation for the order
  /orders/{orderId}/refunds:
    get:
      summary: Retrieve a list of refunds for the order
    post:
      summary: Create a new refund for the order
  /orders/{orderId}/refunds/{refundId}:
    get:
      summary: Retrieve a specific refund for the order
```

This approach allows you to evolve your API over time without breaking existing clients.

## Going beyond RESTful principles

While adhering to RESTful principles is essential, it's also important to consider the practicalities of API design. Here are some detailed topics we'll explore in future articles:

1. [**Pagination**](/api-design/pagination): How to handle large collections of resources. Should you use offset-based pagination, cursor-based pagination, or something else?
2. [**Filtering and searching**](/api-design/filtering-responses): How to allow clients to filter and search resources efficiently.
3. [**Error handling**](/api-design/errors): How to communicate errors effectively and consistently.
4. **Versioning**: How to version your API while maintaining backward compatibility.
5. **Security**: How to secure your API using authentication and authorization mechanisms.
6. **Rate limiting**: How to protect your API from abuse by limiting the number of requests clients can make.
7. **Webhooks**: How to implement webhooks for real-time notifications.

Be sure to check back for more insights on API design.