API Design Guidance: Extended CRUD

A common situation in API design is the need to go beyond the typical CRUD (create-read-update-delete) interaction model. For example, some APIs may need to support actions such as submit, approve, and decline. With our somewhat limited HTTP methods, we must find new ways to offer the extended lifecycle while honouring the HTTP specification. Let’s take a look at how we can extend our API designs beyond the typical CRUD model.

This is a multi-part series on API design guidance, where we take a look at tricks and hidden troubles in API designs and how to avoid them. While these articles may not be exhaustive, they will serve to identify common patterns and anti-patterns in API design.

Use Cases for Extending the CRUD Lifecycle

The most common use case for extending the CRUD pattern is for capturing state transition rules, such as for business workflows, into the API design.

Separating state transitions for workflows to go beyond CRUD offer several advantages, including:

  • Our APIs are able to enforce fine-grained access control at the API management layer, since each specific action is a unique URL that can be assigned different permission requirements. We are able to decouple our access rights from the code, giving us flexibility to further restrict endpoint access without requiring code changes to enforce these restrictions
  • Through the use of hypermedia links, consumers are able to understand what action(s) are currently available to the client based on the user’s permissions (e.g. an editor might be able to approve, decline, and publish while an author may only be able to submit an article as a draft) through the presence or absence of links to each of our lifecycle endpoints
  • The workflow our API supports is more explicit, as we don’t have to look at the PATCH endpoint to understand the valid status values we can update, along with the state machine rules of what is allowed to be transitioned-to and when

Extended CRUD Examples

Consider a content management system that managed a resource collection called articles that now needs to add basic review and approval workflows. Additional operations may be provided to facilitate the workflow, such as:

  • POST /articles/{articleId}/submit
  • POST /articles/{articleId}/approve
  • POST /articles/{articleId}/decline
  • POST /articles/{articleId}/publish

For some state transitions you may wish to accept a property such as reason that describes the purpose or reason for the transition. For transitions that do not require additional details, the request body may be empty.

What About PATCH or PUT with a Status?

One option that is taken is to use a status field that is modified via a PATCH request. This has several limitations, including:

  • Lack of auditability through the request log. All PATCH requests to modify an article appear the same in a request log. Being unable to differentiate if an approval was conducted limits the auditability of API requests. Separating each step in the workflow as a separate operation improves auditability of API usage by reviewing the request logs
  • Lack of fine-grained access control. Separate operations for each workflow state transition enables more fine-grained access control at the API gateway. We can ensure that editors are given appropriate scopes for approvedecline, and publish while limiting authors to createedit, and submit scopes.

Wrap-Up

Your API designs don’t need to be limited to CRUD alone. By selecting an appropriate HTTP method and adopting a URL convention as part of your standards and practices, you can extend your resource lifecycles to mimic a particular workflow, offer custom state transitions, or handle any other kind of design need for resource collections and instances. This approach is quite flexible, so we will build on this pattern for some of our upcoming design guidance articles as well.