As a developer, this leads me to the question: Can we also use storytelling with our REST APIs and will this make them easier to understand? Perhaps storytelling can be used to better communicate the functionality of the interfaces?
My first direct contact with the topic of storytelling was several years ago. It was during an internal conference. A colleague introduced the topic. It was a matter close to his heart. All the colleagues who attended this presentation were enthusiastic and talked about it non-stop. Unfortunately, I had opted for a different presentation. A more technical one, because how could storytelling help me as a developer? Fortunately, his talk was recorded. So storytelling enchanted me too. I still remember that lecture today. How many talks do you remember from the last few years?
Is storytelling even possible with REST APIs?
The connection of a REST API can be as complex as you like. The interface itself and its domain must be understood. Adhering to best practices helps us to understand the interface technically more quickly. But often the greater effort is in understanding the domain. Can storytelling help us with this?

Stories do not have to be static, but can also be dynamic, just like our interfaces. Examples of this are computer games that have a dynamic storyline, but also books. As a child, I had a detective book where you could decide for yourself at the end of the paragraph how the story would continue - similar to Figure 1. This allowed you to influence the course of the story. The central theme is important.
HATEOAS - Hypermedia as the Engine of Application State - can be used for storytelling with REST APIs. It is described by Roy Fielding in his doctoral thesis as the most important feature of a REST API. With HATEOAS, the server sends meta information for the client in addition to the actual data. This creates a loose coupling between the client and the server. The client uses this meta information for navigation. Isn't that similar to my detective book? The interface is the book, a resource is the paragraph of the story and the meta information, the references where the story continues.
The additional meta information helps to better understand the domain and the client does not need to understand the domain in as much detail as the server. This reduces the effort required for the connection.
{
"_links" : {
"self" : {
"href" : "https://my-service/v1/invoices/123456"
},
"createCreditNote" : {
"href" : "https://my-service/v1/invoices/123456/credit-notes"
}
},
"invoiceId" : "2022-007",
"orderId" : "3465756245",
...
}
Code Snippet 1: Response of the resource "invoices" with different response options
Stories from REST APIs are part of everyday developer life
The stories that a REST API tells are probably not as interesting as those of a detective. But they are an important part of our everyday developer life. In one project, we developed a microservice whose (short) stories revolve around the topic of invoices. The information of an invoice is, so to speak, a paragraph of an independent story. Depending on the content of the story, there are different ways to continue it. For example, if an invoice has already been canceled, there is only a reference to the credit note. Otherwise, there would be a reference to the creation of the credit note. In the future, however, there will be further action lines for the invoice. These include, for example, the invoice correction.
The code example shows an exemplary answer. All other action lines for the invoice are shown under the "_left" attribute. In this case, a credit note can be created for this invoice. With HATEOAS, it is also common for each resource to have a reference to itself.
The prevailing opinion is: The implementation of HATEOAS involves a great deal of effort
At first glance, the implementation of HATEOAS increases the workload. However, the additional effort is less than you might think at first glance. Especially in the Spring environment and when using Spring HATEOAS.
import org.springframework.hateoas.RepresentationModel;
public class InvoiceDO extends RepresentationModel {
private String invoiceId;
private String orderId;
// Getter and Setter
}
Code Snippet 2: The only change in the model is the inheritance of RepresentationModel.
@RestController@RequestMapping(value = "/v1/invoices", produces = MediaType.APPLICATION_JSON_VALUE)
public class InvoiceApiResource {
// other methods and fields
@GetMapping("/{invoiceNo}")
public ResponseEntity getInvoice(
@PathVariable("invoiceNo") @NotNull final Integer invoiceNo) {
final var invoiceResult = invoicesBF.getInvoiceInformation(invoiceNo);
if (invoiceResult.isSuccess()) {
final var invoiceBE = invoiceResult.getValue();
final var invoiceNo = invoiceBE.getId().intValue();
final var invoiceDO = invoiceMapperBA.map(invoiceBE);
final var doesCreditNoteExist =
InvoiceTypeDO.INVOICE.equals(invoiceDO.getType())
&& invoicesBF.doesCreditNoteExistForInvoice(invoiceNo);
invoiceDO
.add(linkTo(methodOn(InvoiceApiResource.class).getInvoice(invoiceNo)).withSelfRel())
.addIf(InvoiceTypeDO.INVOICE.equals(invoiceDO.getType()) && !doesCreditNoteExist,
() -> linkTo(methodOn(InvoiceApiResource.class).createCreditNote(invoiceNo)).withRel(
"createCreditNote"))
.addIf(
doesCreditNoteExist,
() -> linkTo(methodOn(InvoiceApiResource.class).getCreditNotes(invoiceNo)).withRel(
"creditNotes"))
.addIf(
InvoiceTypeDO.CREDIT_NOTE.equals(invoiceDO.getType()),
() -> linkTo(methodOn(InvoiceApiResource.class).getInvoice(invoiceBE
.getOriginId()
.intValue())).withRel("originInvoice"));
return ResponseEntity.ok(invoiceDO);
} else {
return ResponseEntity.notFound().build();
}
}
@GetMapping("/{invoiceNo}/credit-notes")
public ResponseEntity getCreditNotes(@NotNull final Integer invoiceNo) { }
@PostMapping("/{invoiceNo}/credit-notes")
public ResponseEntity createCreditNote(@NotNull final Integer invoiceNo) { }
}
Code Snippet 3: Example of a RestController with focus on the components required for the use of HATEOAS
The code in lines 15 - 33 results from the use of HATEOAS. Additional code that also needs to be maintained and understood. However, the advantage here is that the code was developed by the domain experts. No client has to understand this logic and implement it themselves. This eliminates this source of error. If an error does creep in, it can be fixed at a central location.
REST APIs can tell stories. These stories help the user to better understand the domain. Not only we, but also our customer was enthusiastic about it. He recognized the added value and was pleased with how easily we made his domain accessible. The feared increased effort during implementation was negligible. The additional code works without any problems and did not have to be adapted again. This is mainly due to the really good implementation of Spring HATEOAS. Spring HATEOAS has convinced us and we can only recommend everyone to try it out for themselves.
Can you imagine using HATEOAS for the next interface? Or why not?



