Umbraco

Aligning Package Behavior with the Umbraco Content Delivery API

Andy Butland
Written by Andy Butland

Probably the new feature generating the most buzz for the new Umbraco 12 release is the native REST API, known as the Content Delivery API. This allows you to retrieve content as JSON, better supporting Umbraco usage in headless scenarios. Last year we added a headless API to Umbraco Forms, allowing you to retrieve a Form definition and post submissions in this manner.  With Umbraco 12, and Forms 12, we want to make sure that these APIs are aligned as closely as possible, so they are familiar to work with and when moving between them.

Some of those changes have been “behind the scenes” updates – things like ensuring we use the same libraries for serialization and API documentation.  More publicly, there are a few features of the Content Delivery API that we’ve looked to add support for in Forms, and these could well be useful in other contexts – whether packages or custom solutions.  So that’s the topic for this blog post.

Returning Custom Types for Expanded Requests

Anyone that’s developed a property editor for Umbraco will likely be familiar with the concept of a property value converter.  These can be used to convert the raw data stored in the database for a content property into a strongly typed model, suited for rendering that property in a Razor page template

There’s a similar feature available for the Content Delivery API, which you use by implementing the IDeliveryApiPropertyValueConverter interface in your property value converter.  It requires you to implement three methods, the most important being ConvertIntermediateToDeliveryApiObject which is responsible for the conversion of the value into the type you define in GetDeliveryApiPropertyValueType.

Implementing this interface in your property value converter is optional.  Without it the Delivery API will expose the raw value that’s stored as property data.

In the case of the "Form Picker" property editor, the raw value stored is the GUID that identifies the form.  As such, by default, for a page with a property using the form picker that has an alias of “contactForm”, we’d get an output looking like this:

So, using this, you could make a request to the Delivery API to get the details for the page, extract the ID for the picked form, and initiate a second request to the Forms API to retrieve the full details of the form.  With both together, you could render the page.

Wouldn’t it be nice though to be able to do this in one request?  Well, you can… by implementing the interface mentioned earlier.  Instead of returning the raw value, we could retrieve the full details of the form and a serialized representation of that.

However, it could be that you don’t want the full details for the request you are making, so it might not be a good idea to bake this behaviour in for every request.  In the case of Forms, it would mean an additional database request and some further server-side work that would be necessary before the Delivery API response could be returned.

Fortunately, there’s a nice option with the Delivery API to allow us to defer this decision to the client making the request – and they can take a call as to whether for a particular case they want the full form details, as opposed to others where they either don’t need it or the ID alone is sufficient.  This is known as output expansion.

As of Umbraco 12 RC 3, the state of the expansion request for a particular property is propagated to the property editor, via the expanding parameter available in the ConvertIntermediateToDeliveryApiObject method signature.  We’ve used this in Forms in the following way.

Firstly, we define a type to represent the form output in the Delivery API.  It contains two properties, the picked form's ID and a form representation that is the same as that retrievable from the Forms API:

If we are not “expanding”, we just populate the form’s ID, leading to an output of:

 And if we are, we fully populate the object:

There is one small downside with this in the repetition of the Id in two places.  But more importantly, we get the two main benefits here:

  • Having a single return type for both expansion options, which adheres to the requirements of the interface and ensures future use of client libraries built from the APIs will be feasible.
  • Re-using and ensuring identical responses between the delivery API and the direct use of the Forms API.

The full details of the Forms implementation of the IDeliveryApiPropertyValueConverter  interface is shown below:

Exposing Routes to Content

For similar reasons of avoiding unnecessary round trips when working with the Delivery API, rather than returning IDs for picked or related content, a route structure is returned.  It looks like this in the output:

In the details of the form definition that we return for the Forms API, there’s a property that defines the ID for the content page selected that the user will be redirected to once the form has been submitted.  For Forms 10 and 11, we expose that as just the ID.

Which serializes as:

In Forms 12 we’ve added a second property that exposes the same information as a route.

Serializing in a consistent way to the other routes available via the Delivery API:

We can utilize some handy builder classes defined in IApiContentRouteBuilder and other CMS classes to populate this value:

And in doing that we have one more way that will help working with the two APIs feel familiar, and clearly part of the same underlying product.

Documenting the API

One final feature to note, that we have opted into for the Forms API for Umbraco 12, is to remove the Forms-specific Swagger document we had previously, and instead group it under the CMS’s Delivery API details.

To make this work, you define a class that defines the Swagger document for your API:

And register this as part of startup:

Finally, decorate your API controllers with the following attribute to ensure they are listed under the appropriate definition:

Summing Up

Including the techniques shown here in either your own APIs, or property editors that will now be exposing data via the Delivery API, should help with making working them as familiar as possible for the consumers. They are part of the efforts we are making to ensure the APIs available from CMS, Forms, and in future other commercial packages and Heartcore are aligned in this way, providing a consistent experience across the range of Umbraco products and services.