API Design Guidance: Workflows

While most APIs may be simple, offering anything from a standalone operation to a CRUD-based lifecycle, there are times when a specific workflow needs to be modeled in an API. Rather than forcing clients to hard-code the state transitions and other associated logic, hypermedia links may be used to inform the client on what transitions are valid based on the server-side state. It also helps us to support complex or indeterminate workflows, where state transition logic isn’t easily replicated on the client.

As we continue this series, let’s look at how hypermedia-based workflows can be designed into our APIs for most dynamic client-server interactions.

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.

Introducing Hypermedia-based Workflows

APIs with hypermedia extend the conversation of your API by offering runtime discovery of capabilities. They help API consumers realise what is possible – and what isn’t – when using your API.

Offering information about state transition in hypermedia links prevents clients from hard-coding the business logic, instead relying on the absence or presence of links to determine what options are next.

Let’s walk through an API design that manages the publication of articles. The articles must first be approved by an editor before they are published. Let’s create a new article:

POST /articles
Content-Type: application/json

{ "title":"My article", "content":"..." }

We then receive a 201 Created upon successful creation, including hypermedia links that indicate what we may do next:

HTTP/1.1 201 Created
Content-Type: application/json

{
  "title": "My article",
  "content": "...",
  "status": "draft",
  "_links": [
    { "rel":"self", "href":"/articles/12345" },
    { "rel":"update", "href":"/articles/12345" },
    { "rel":"submit", "href":"/articles/12345/submit" },
  ]
}

The presence of the update hypermedia link indicates that the article is still allowed to be updated. The presence of the submit link indicates that the article hasn’t been submitted yet and that it is still possible.

Moving the Workflow Forward Through State Transitions

Let’s assume that the author has completed the initial draft and submits it to the editor for review. The client uses the existance of the submit link and its corresponding link to submit the article for review.

POST /articles/12345/submit
Content-Type: application/json

{ "message": "Have a look and let me know if this meets what you expected", ... }

The API determines that the article is in the proper state to allow the request, so it returns a success response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "title": "My article",
  "content": "...",
  "status": "submitted",
  "_links": [
    { "rel":"self", "href":"/articles/12345" }
  ]
}

Based on the links returned, we are unable to perform any further updates until it has been approved or declined.

Collaboration through Hypermedia Workflows

Now, let’s consider that the editor receives a notification that the article is ready for review. The editor retrieves the article via the API:

GET /articles/12345
Content-Type: application/json

She then reviews the article, with hypermedia links provided that includes both actions available based the current state of the article, along with her role as editor:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "title": "My article",
  "content": "...",
  "status": "draft",
  "_links": [
    { "rel":"self", "href":"/articles/12345" },
    { "rel":"approve", "href":"/articles/12345/approve" },
    { "rel":"decline", "href":"/articles/12345/decline" },
  ]
}

The editor may then submit an approval:

POST /articles/12345/approve
Content-Type: application/json

{ "message": "Looks good", ... }

Or, perhaps she decides to decline the article as it needs more editing:

POST /articles/12345/decline
Content-Type: application/json

{ "message": "Please update your facts with links to the source", ... }

The state of the article changes appropriately, resulting in new links being available and others being hidden, based on the server-side state.

This is the concept of Hypermedia as the Engine of Application State (HATEOAS) – server-side state is sent to the client, including links that indicate what may and may not be done based on any workflow rules, along with links that reflect the actions available based on the assigned role of the current user.

Avoiding the N+1 Query Problem Over HTTP

By including hypermedia into resource representations, API designers are free to stop trying to include everything in every API response. API endpoints can focus on doing one thing properly, using hypermedia links to reference other details that may be fetched by API consumers if and when needed. This creates a loose-coupled API by separating out capabilities into separate, yet related, endpoints rather than forcing every piece of data to be included within each response.

However, if thoughtful design isn’t applied, clients may be left with an N+1 query problem – forced to request additional details for each parent and/or child resource, resulting in a large number of API requests.

Wrap-Up: Finding the Balance in API Design

As an API designer, it is your job to work with all stakeholders to ensure that API responses are complete and useful for the intended use cases. This helps to avoid anemic resources that represent a single database table, or a single resource that represents an entire database in a single response. You may also decide to offer field selection and deep fetching patterns to reduce this case. More on these patterns in a future article.