Common RESTful API Design Mistakes and Their Remedies

Designing RESTful APIs is an art that requires attention to detail, a deep understanding of HTTP, and a commitment to the principles laid out by Roy Fielding. However, many APIs that claim to be RESTful fall short of these principles. In this blog, let’s explore 20 common mistakes and their remedies :

1. Misuse of HTTP Methods

  • Non-RESTful: POST /getSubscription?id=123
  • RESTful Remedy: GET /subscriptions/123

2. Ignoring Caching Opportunities

  • Non-RESTful: Responses without Cache-Control.
  • RESTful Remedy: Include Cache-Control: max-age=3600 in GET responses for subscriptions.

3. Inconsistent Error Handling

  • Non-RESTful: {"error": "It failed"}
  • RESTful Remedy: {"error": {"code": 404, "message": "Subscription not found"}} with a 404 status code.

4. Lack of Resource Nesting

  • Non-RESTful: /subscriptions, /users
  • RESTful Remedy: /users/123/subscriptions to list subscriptions for user 123.

5. Overuse of Query Parameters

  • Non-RESTful: /subscriptions?userId=123
  • RESTful Remedy: /users/123/subscriptions

6. Forgetting Pagination

  • Non-RESTful: Returning all subscriptions in one call.
  • RESTful Remedy: /subscriptions?page=1&limit=10

7. Failing to Version the API

  • Non-RESTful: Directly modifying the API structure.
  • RESTful Remedy: /v2/subscriptions/123

8. Underutilizing HTTP Headers

  • Non-RESTful: Passing API keys as query parameters.
  • RESTful Remedy: Authorization: Bearer <token>

9. Not Supporting Conditional Requests

  • Non-RESTful: Ignoring use of ETag or Last-Modified.
  • RESTful Remedy: Respond with ETag header and honor If-None-Match requests.

10. Poor Resource Identification

  • Non-RESTful: Ambiguous identifiers.
  • RESTful Remedy: /subscriptions/{uuid} where uuid is a unique identifier.

11. Ignoring Idempotency in Non-GET Requests

  • Non-RESTful: POST /subscriptions/123/renew leading to multiple renewals.
  • RESTful Remedy: PUT /subscriptions/123/renewal to renew idempotently.

12. Not Leveraging HATEOAS

  • Non-RESTful: { "subscriptionId": "123", "status": "active" }
  • RESTful Remedy:

{ "subscriptionId": "123", "status": "active", "_links": { "self": { "href": "/subscriptions/123" }, "cancel": { "href": "/subscriptions/123/cancel" } } }

13. Mixing Singular and Plural Nouns

  • Non-RESTful: Inconsistent use of singular and plural in resource paths.
  • RESTful Remedy: Consistently use plural nouns for resource names: /subscriptions for collection routes and /subscriptions/{id} for specific entities.

14. Exposing Internal Implementation Details

  • Non-RESTful: Revealing database IDs or internal structure through APIs.
  • RESTful Remedy: Use abstracted, meaningful identifiers: /subscriptions/12345 where 12345 is an opaque identifier.

15. Not Providing Filtering, Sorting, or Searching Capabilities

  • Non-RESTful: Forcing clients to download entire data sets.
  • RESTful Remedy: Implement query parameters for server-side operations: /subscriptions?status=active&sortBy=startDate

16. Lack of Media Type Negotiation

  • Non-RESTful: Only supporting application/json.
  • RESTful Remedy: Use the Accept header for format requests. Respond to Accept: application/xml for XML format.

17. Incomplete or Missing Documentation

  • Non-RESTful: APIs without comprehensive documentation.
  • RESTful Remedy: Provide detailed, up-to-date documentation portal.

18. Not Utilizing PATCH for Partial Updates

  • Non-RESTful: Using PUT for all updates.
  • RESTful Remedy: Implement PATCH for partial resource updates.

19. Treating Collections and Individual Resources the Same

  • Non-RESTful: Same endpoint for collection and individual resource operations.
  • RESTful Remedy: Differentiate endpoints for collections and individual resources: POST /subscriptions for creating and GET /subscriptions/123 for fetching.

20. Not Handling Asynchronous Operations Properly

  • Non-RESTful: Expecting synchronous completion of long-running tasks.
  • RESTful Remedy: Use asynchronous patterns with initial 202 Accepted responses: POST /subscriptions/123/renew returns 202 Accepted with a Location header for status checks.

Patterns for designing self healing apps

Self healing is important concern while developing an application. For example, if a downstream service is not available , how can the app handle this situation? Will it retry more for the service which is already down or will it understand the situation and stop hammering a failing service. What if there is failure in one subsystem which can sometime cascade, for example a thread or a socket not getting freed in timely manner can result to cascading failures. All too often, success path is well tested but not the failure path. There are many patterns for handling these failures but here are few must have pattern to gracefully handle these situation.

PatternPremiseAkaHow does the pattern mitigate?
Retry failed operations with retry strategy (Retry Pattern)
Implementation Detail
Many faults are transient and may self-correct after a short delay. Have a retry pattern with strategy of increasing delay. “Maybe it’s just a blip”Allows configuring automatic retries.
Protecting failing app with Circuit Breaker
(Circuit-breaker)
Implementation Detail
When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover.
“Stop doing it if it hurts”

“Give that system a break”
Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold.
Better caller experience with timeout
(Timeout)
Implementation Detail
Beyond a certain wait, a success result is unlikely.“Don’t wait forever”Guarantees the caller won’t have to wait beyond the timeout.
Isolate critical resources (Bulkhead Isolation)
Implementation Detail
A Ship should not sink because there is hole in one place. When a process faults, multiple failing calls can stack up (if unbounded) and can easily swamp resource (threads/ CPU/ memory) in a host.

This can affect performance more widely by starving other operations of resource, bringing down the host, or causing cascading failures upstream.
“One fault shouldn’t sink the whole ship”Constrains the governed actions to a fixed-size resource pool, isolating their potential to affect others.
Throttle clients with Rate Limit (Rate-limit)
 Implementation Detail
Limiting the rate a system handles requests is another way to control load.

This can apply to the way your system accepts incoming calls, and/or to the way you call downstream services.
“Slow down a bit, will you?”Constrains executions to not exceed a certain rate.
Fallback
Implementation Detail
Things will still fail – plan what you will do when that happens.“Degrade gracefully”Defines an alternative value to be returned (or action to be executed) on failure.