Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Discover how to customize the Content Delivery API's response for your custom property editors.
Out of the box, the Delivery API supports custom property editors, ensuring they are rendered alongside the built-in ones in Umbraco. However, if the output generated by your property editor isn't optimal for a headless context, you have the ability to customize the API response. This customization won't impact the Razor rendering, allowing you to tailor the Content Delivery API response according to your specific requirements.
In this article, we'll look into how you can work with the IDeliveryApiPropertyValueConverter
interface and implement custom property expansion for your custom property editors.
The examples in this article revolve around the fictional My.Custom.Picker
property editor. This property editor stores the key of a single content item and is backed by a property value converter.
We will not dive into the details of creating a custom property editor for Umbraco in this article. If you need guidance on that, please refer to the Creating a Property Editor and Property Value Converters articles.
To customize the output of a property value editor in the Delivery API, we need to opt-in by implementing the IDeliveryApiPropertyValueConverter
interface.
The code example below showcases the implementation of this interface in the property value converter for My.Custom.Picker
. Our focus will be on the methods provided by the IDeliveryApiPropertyValueConverter
, as they are responsible for customizing the Delivery API response.
Towards the end of the example, you will find the response models that we will be using.
The IsConverter()
and GetPropertyValueType()
methods are inherited from the PropertyValueConverterBase
class, which is covered in the Property Value Converters article.
The Implementation of the IDeliveryApiPropertyValueConverter
interface can be found in the following methods:
GetDeliveryApiPropertyCacheLevel()
: This method specifies the cache level used for our property representation in the Delivery API response.
GetDeliveryApiPropertyCacheLevelForExpansion()
: This method specifies the cache level used for our property representation in the Delivery API response when property expansion is applied.
GetDeliveryApiPropertyValueType()
: This method defines the value type of the custom property output for the Delivery API response.
ConvertIntermediateToDeliveryApiObject()
: This method converts the value from the property editor to the desired custom object in a headless context.
In the given example, the content key (Guid
value) is used when rendering with Razor. This is sufficient because Razor provides full access to the published content within the rendering context. In a headless context, we do not have the same access. To prevent subsequent round-trips to the server, we create a richer output model specifically for the Delivery API.
The following example request shows how our custom implementation is reflected in the resulting API response. In this case, our custom property editor is configured under the alias "pickedItem"
.
Request
Response
Property expansion allows us to conditionally add another level of detail to the Delivery API output. Usually, these additional details are "expensive" to retrieve (for example, requiring database access to populate). By applying property expansion, we provide the option for the caller of the API to opt-in explicitly to this "expensive" operation. From the caller's perspective, the alternative might be an even more expensive additional round-trip to the server.
In our example, property expansion is implemented within ConvertIntermediateToDeliveryApiObject()
. By considering the value of the expanding
parameter, we can modify the BuildDeliveryApiCustomPicker()
method as follows:
If the expanding
parameter is false
, the method returns the same shallow representation of the referenced content item as before. Otherwise, we retrieve the corresponding IPublishedContent
and construct our response object accordingly.
To see the expanded output in the API response, we need to add the expand
query parameter to our request. We can use either ?expand=properties[$all]
to expand all properties or ?expand=properties[pickedItem]
to expand the specific 'pickedItem'
property.
Request
Response
The itemDetails
property of the pickedItem
in the JSON response contains the additional details of the selected content item.
Learn how to extend the Content Delivery API with custom selecting, filtering, and sorting options for the multi-item-based endpoint.
The Delivery API allows you to retrieve multiple items by utilizing the /umbraco/delivery/api/v2/content
endpoint. With the built-in query parameters, you have the flexibility to get any number of content nodes based on your needs. For a comprehensive list of supported query options, please refer to the Endpoints section.
For the query endpoint, we have created a new Examine index (DeliveryApiContentIndex) that facilitates fast retrieval of the desired content. This index ensures quick indexing and searching of data, with the possibility for future extensions.
In this article, we'll explore creating custom selecting, filtering, and sorting options to enhance the querying capabilities of the Delivery API.
Let's take a look at an example of using the query endpoint with query parameters for fetch
, filter
, and sort
. A request might look like this:
The placeholders in the example (xxx
, yyy
, etc.) represent the values that each query option evaluates in order to determine the suitable query handler.
You can include only one fetch
parameter, while multiple filter
and sort
parameters are allowed. Additionally, the order of the sort
parameters influences the sorting behaviour. Refer to the Query parameters section for the currently supported options.
The implementation of each querying option consists of a class for indexing the data into the DeliveryApiContentIndex and another one for handling the query. By implementing the IContentIndexHandler
interface, you can control how your relevant data is indexed and made available for querying through our index. And you can customize the querying behaviour to suit your needs by implementing the ISelectorHandler
, IFilterHandler
, and ISortHandler
interfaces.
In the following sections, we will explore the implementation details of creating custom querying functionality for the Delivery API.
Selectors handle the fetch
part of a query.
To showcase how to build a custom selector, consider a site structure with a few blog posts. A post is linked to an author, which is another content item.
Authors can be marked as 'Featured' using a toggle, granting them additional visibility and recognition. We will use this marker as part of the indexing implementation for our selector option.
The following example demonstrates the implementation of an AuthorSelector
, which allows you to customize the querying behaviour specifically for finding all featured authors. This class contains both indexing and querying responsibilities. However, keep in mind that it is generally recommended to separate these responsibilities into dedicated classes.
The AuthorSelector
class implements the ISelectorHandler
and IContentIndexHandler
interfaces.
ISelectorHandler
allows for handling the fetch
value in API queries through the CanHandle()
and BuildSelectorOption()
methods.
CanHandle()
determines if the given fetch
query corresponds to the "featuredAuthors"
value.
BuildSelectorOption()
constructs the selector option to search for authors with a positive value (for example, "y"
) in a "featured"
index field.
The GetFields()
and GetFieldValues()
methods each play a role in defining how the data should be indexed and made searchable.
GetFields()
defines the behaviour of fields that are added to the index. In this example, the "featured"
field is added as a "raw" string for efficient and accurate searching.
GetFieldValues()
is responsible for retrieving the values of the defined index fields. In this case, the "featured"
field of content items of type "author"
. It creates an IndexFieldValue
with the appropriate field value ("y"
for featured, "n"
otherwise), which will be added to the index.
Since our custom query option modifies the index structure, we will need to rebuild the DeliveryApiContentIndex. You can find it by navigating to the "Examine Management" dashboard in the "Settings" section. Once rebuilt, we can make a request to the Delivery API query endpoint as follows:
Request
Response
Filters handle the filter
part of a query.
Staying within the topic of blog posts and their authors, we will create a custom filter to find posts by specific author(s).
This filter allows specifying the desired author(s) by their key (Guid
) in an author:
filter option. Multiple authors can be included by listing their keys as comma-separated-values, like:
Request
The response will include the blog posts associated with the provided authors, enabling us to retrieve only the relevant results from the API.
Response
Our filter implementation follows a similar structure to the custom selector we discussed earlier. We continue to utilize the IContentIndexHandler
interface, but this time we introduce the IFilterHandler
. This combination gives us flexibility and control over the filtering behaviour.
The procedure remains the same - we store and query the author key in a new "authorId"
field within the index. Consequently, we will need to rebuild the index to reflect the changes.
To illustrate the implementation, consider the following code example:
The principal difference from the selector is that the filter implements BuildFilterOption()
instead of BuildSelectorOption()
. Here, the filter performs an exact match for any specified Guid
in the query. Efficiently, this makes the filter perform an OR
operation against the index.
Since we need to perform an exact match, the index field (authorId
) is once again defined as a "raw" string. Other options include "analyzed" and "sortable" strings. These support "contains" searches and alpha-numeric sorting, respectively.
When implementing a filter, you can use the following operators: Is
, IsNot
, Contains
, DoesNotContain
, GreaterThan
, GreaterThanOrEqual
, LessThan
and LessThanOrEqual
.
The range operators (the latter four) only work with number and date fields - FieldType.Number
and FieldType.Date
respectively.
It is possible to pass multiple values to each operator, and these values will be treated inclusively as an or operator. For example, if tag1
and tag2
were passed into a filter using the Is
operator, any document containing either tag1
or tag2
would return. The request for this might look like this:
If you require this functionality to be restrictive i.e. tag1
and tag2
, then the current approach would be to chain the custom filter. The request would change to look more like this:
Finally, we can also add custom handling for the sort
part of the query.
We'll add a custom sort handler that allows us to sort blog posts based on a custom "publishDate"
Date Picker property. The implementation will allow for sorting the posts in ascending or descending order.
This sorting should only be used with query results that have a published date to ensure accurate results.
To demonstrate this, consider the following implementation example:
The implementation follows the same structure as the other examples, defined by the IContentIndexHandler
and ISortHandler
interfaces.
One point to highlight is that we store the "publishDate"
value as a "date" field in the index, which allows for correct date sorting.
Once more, when adding fields to the index, we need to rebuild it to reflect the changes.
In the following example request, we also apply the author filter to retrieve only "blogpost"
content nodes, which we know have the "publishDate"
field.
Request
Response
Get started with the Content Delivery API.
The Content Delivery API delivers headless capabilities built directly into Umbraco. It allows you to retrieve your content items in a JSON format and use your preferred technology to present them in different channels.
The feature preserves a friendly editing experience in Umbraco while ensuring a performant delivery of content in a headless fashion. With the different extension points, you can tailor the API to fit a broad range of requirements.
The Delivery API is an opt-in feature in Umbraco. It must be explicitly enabled through configuration before it can be utilized.
Open your project's appsettings.json
.
Insert the DeliveryApi
configuration section under Umbraco:CMS
.
Add the Enabled
key and set its value to true
.
Once the Content Delivery API is enabled, the next step is to rebuild the Delivery API content index (DeliveryApiContentIndex). This can be done using the Examine Management dashboard in the Settings section of the Umbraco Backoffice.
Access the Umbraco Backoffice.
Navigate to the Settings section.
Open the Examine Management dashboard.
Scroll down to find the Tools.
Use the Rebuild index button.
Once the index is rebuilt, the API can serve the latest content from the multiple-items endpoint.
When the Delivery API is enabled in your project, all your published content will be made available to the public by default.
A few additional configuration options will allow you to restrict access to the Delivery API endpoints and limit the content returned.
Find a description of each of the configuration keys in the table below.
Are you using Umbraco Cloud?
When hosting your Umbraco website on Umbraco Cloud, security should always be prioritized for sensitive information like API keys. Rather than storing it as plain text in the appsettings.json
file use the Umbraco Cloud's built-in secrets management. This feature lets you store and manage sensitive data, keeping your API key safe from potential exposure or unauthorized access. To learn more about implementing secrets management, read the Secrets management documentation.
To test the functionality of the API, you need to create some content first.
Before exploring the API endpoints detailed below, there are a few concepts to know.
The Delivery API output can represent a specific content item or a paged list of multiple items.
When referring to a specific content item in your API requests, the id
parameter always refers to the item’s key (GUID).
GET
/umbraco/delivery/api/v2/content/item/{id}
Returns a single item.
GET
/umbraco/delivery/api/v2/content/item/{path}
Returns a single item.
GET
/umbraco/delivery/api/v2/content/items
Returns single or multiple items by id.
GET
/umbraco/delivery/api/v2/content
Returns single or multiple items.
All endpoints are documented in a Swagger document at {yourdomain}/umbraco/swagger
. This document is not available in production mode by default. For more information read the API versioning and OpenAPI article.
The Content Delivery API provides query parameters that allow you to customize the content returned by the API. The relevant query parameters for each endpoint are already specified in the corresponding documentation above.
In addition to standard parameters like skip
and take
, the API provides different possibilities for the value of the expand
, fields
, fetch
, filter
and sort
parameters. Below are the options that are supported out of the box.
You can extend the built-in selector, filter, and sorting capabilities of the Delivery API by creating custom query handlers.
Refer to the Property expansion and limiting concept for more information about this parameter.
?expand=properties[$all]
All expandable properties on the retrieved content item will be expanded.
?expand=properties[alias1]
A specific expandable property with the property alias alias1
will be expanded.
?expand=properties[alias1,alias2,alias3]
Multiple expandable properties with the specified property aliases will be expanded.
?expand=properties[alias1[properties[nestedAlias1,nestedAlias2]]]
The property with the property alias alias1
will be expanded, and likewise the properties nestedAlias1
and nestedAlias2
of the expanded alias1
property.
Refer to the Property expansion and limiting concept for more information about this parameter.
?fields=properties[$all]
Includes all properties of the retrieved content item in the output.
?fields=properties[alias1]
Includes only the property with the property alias alias1
in the output.
?fields=properties[alias1,alias2,alias3]
Includes only the properties with the specified property aliases in the output.
?fields=properties[alias1[properties[nestedAlias1,nestedAlias2]]]
Includes only the property with the property alias alias1
in the output. If this property is expanded, only the properties nestedAlias1
and nestedAlias2
of the expanded alias1
property are included.
You can apply a selector option to the /umbraco/delivery/api/v2/content
endpoint to query content items based on their structure. The selector allows you to fetch different subsets of items based on a GUID or path of a specific content item. The Delivery API will search all available content items if no fetch
parameter is provided. The following built-in selectors can be used out of the box:
?fetch=ancestors:id/path
All ancestors of a content item specified by either its id
or path
will be retrieved.
?fetch=children:id/path
All immediate children of a content item specified by either its id
or path
will be retrieved.
?fetch=descendants:id/path
All descendants of a content item specified by either its id
or path
will be retrieved.
Only one selector option can be applied to a query at a time. You cannot combine multiple fetch parameters in a single query.
For example, the following API call will attempt to retrieve all the content items that are directly below an item with the id dc1f43da-49c6-4d87-b104-a5864eca8152
:
Request
The filter query parameter allows you to specify one or more filters. These filters must match for a content item to be included in the response. The API provides a few built-in filters that you can use right away with the /umbraco/delivery/api/v2/content
endpoint:
?filter=contentType:alias
This filter restricts the results to content items that belong to the specified content type. Replace alias
with the alias of the content type you want to filter by.
?filter=name:nodeName
Only content items whose name matches the specified value will be returned when this filter is applied. Replace nodeName
with the name of the item that you want to filter by.
The contentType
and name
filters support negation. You can exclude content items from the result set that match the filter criteria using an exclamation mark before the filter value.
For example, you can fetch all content items that are not of type article
like this: ?filter=contentType:!article
.
?filter=createDate>date
Only content items created later than the specified value will be returned when this filter is applied. Replace date
with the date that you want to filter by.
?filter=updateDate>date
Only content items updated later than the specified value will be returned when this filter is applied. Replace date
with the date that you want to filter by.
The createDate
and updateDate
filters support both "greater than", "greater than or equal", "less than" and "less than or equal":
Use > for "greater than" filtering.
Use >: for "greater than or equal" filtering.
Use < for "less than" filtering.
Use <: for "less than or equal" filtering.
Multiple filters can be applied to the same request in addition to other query parameters:
Request
This technique can also be used to perform range filtering. For example, fetch articles created in 2023:
Request
Specifying how the results should be ordered, can be achieved using the sort
query option. You can use this parameter to sort the content items by fields, including create date
, level
, name
, sort order
, and update date
. For each field, you can specify whether the items should be sorted in ascending (asc) or descending (desc) order. Without a sort
query parameter, the order of the results will be determined by the relevance score of the DeliveryApiContentIndex for the search term.
?sort=createDate:asc/desc
An option to sort the results based on the creation date of the content item in either asc
or desc
order.
?sort=level:asc/desc
An option to sort the results based on the level of the content item in the content tree in either asc
or desc
order.
?sort=name:asc/desc
An option to sort the results based on the name of the content item in either asc
or desc
order.
?sort=sortOrder:asc/desc
An option to sort the results based on the sort order of the content item in either asc
or desc
order.
?sort=updateDate:asc/desc
An option to sort the results based on the last update date of the content item in either asc
or desc
order.
Different sorting options can be combined for the /umbraco/delivery/api/v2/content
endpoint, allowing for more advanced sorting functionality. Here is an example:
Request
The Delivery API has been designed for extensibility, offering multiple extension points that provide greater flexibility and customization options. These extension points allow you to tailor the API's behavior and expand its capabilities to meet your specific requirements.
You can find detailed information about the specific areas of extension in the articles below:
.NET imposes a limit on the depth of object nesting within rendered JSON. This is done to detect cyclic references. Learn more about it in the official .NET API docs.
If the limit is exceeded, .NET will throw a JsonException
.
The content models might be so deeply nested that the Delivery API produces JSON that exceeds this limit. If this happens, the JsonException
will be logged and shown in the Umbraco log viewer.
To handle this we have to change the limit. Since the Delivery API has its own JSON configuration, we can do so without affecting the rest of our site.
Add the following using
statements to Program.cs
:
Add the following code snippet to the Program.cs
file:
The Content Delivery API provides a powerful and flexible way to retrieve content from the Umbraco CMS. There are, however, certain limitations to be aware of.
In this section, we will discuss some of the known limitations of the API, and how to work around them if necessary.
The Delivery API supports protected content and member authentication. This is an opt-in feature. You can read more about it in the Protected Content in the Delivery API article.
If member authentication is not explicitly enabled, protected content is ignored and never exposed by the Delivery API.
As a result, lifting protection from a content item requires an additional step to ensure it becomes accessible through the Delivery API. The recommended way is to publish the content item again. Alternatively, you can manually rebuild the DeliveryApiContentIndex
to reflect the changes.
Certain limitations are associated with some of the built-in property editors in Umbraco. These limitations are listed below.
Internal links may be insufficient in a multi-site setup when outputting the Rich Text Editor content as HTML (the default format). There is a possibility that this limitation may be addressed in future updates. However, consider the alternative approach to rendering the Rich Text Editor content as JSON.
The Member Picker property editor is not supported in the Delivery API to avoid the risk of leaking member data.
The Content Picker property editor is not supported in the Delivery API when configured for Members. This is due to the concern of potentially leaking member data.
DisallowedContentTypeAliases
When changing the content type aliases in the Umbraco:CMS:DeliveryApi:DisallowedContentTypeAliases
configuration setting, DeliveryApiContentIndex
should be rebuilt. This ensures that disallowed content types are not exposed through the Delivery API.
Alternatively, the relevant content items can be republished. This will ensure that changes are reflected, eliminating the need to rebuild the index.
Using the Media Delivery API.
The Media Delivery API specification is created to mimic that of the Content Delivery API. However, the default implementation of this specification is limited and does not support the entire specification.
Unlike the Content Delivery API, the Media Delivery API does not feature an extension model for querying.
The reasoning behind is that third-party media systems might support a complete implementation of the specification. If the demand rises, the default implementation might eventually cover the entire specification.
To use the Media Delivery API you must first enable it. Even if the Content Delivery API is enabled, the Media Delivery API remains disabled by default.
The Media Delivery API is enabled by adding the Media
section to the DeliveryApi
configuration in appsettings.json
:
As this configuration sample illustrates, it is possible to restrict public access to media independently from content. As with the Content Delivery API, media is publicly accessible by default when the Media Delivery API is enabled.
The Media
configuration can only become more restrictive than the DeliveryApi
configuration:
If DeliveryApi:Enabled
is false
, the DeliveryApi:Media:Enabled
configuration option has no effect. The Media Delivery API cannot be enabled on its own.
If DeliveryApi:PublicAccess
is false
, the DeliveryApi:Media:PublicAccess
configuration option has no effect. The Media Delivery API cannot be publicly available if the Content Delivery API is not.
The Media Delivery API can either be queried for a specific media item or a paged list of multiple items.
In the Media Delivery API, id
parameters always refer to media item keys (Guid
), not node ids (integer
).
GET
/umbraco/delivery/api/v2/media/item/{id}
Returns a single item.
GET
/umbraco/delivery/api/v2/media/item/{path}
Returns a single item.
GET
/umbraco/delivery/api/v2/media/items
Returns single or multiple items by id.
GET
/umbraco/delivery/api/v2/media
Returns single or multiple items.
Fetch a media item by its ID:
Fetch a media item inside a folder structure by its full path, and expand its author
property:
Fetch two media items by their ids:
Fetch the first 10 media items of type Image
at root level. Return the found items sorted by name ascending:
Fetch the first 5 media items inside a folder structure. Return only items of type Image
whose item names contain "size".
The Media Delivery API outputs the JSON structure outlined below to represent media items:
Item path
, createDate
, updateDate
, id
, name
, and mediaType
are always included in the response.
url
, extension
and the size in bytes
are included for all files (not for folders).
width
and height
(in pixels) are included for most images.
Depending on Umbraco Data Type configuration, focalPoint
and crops
are included for most images.
Additional editorial properties from the media type can be found in the properties
collection.
How to use member authorization with the Delivery API to access protected content.
Umbraco allows for restricting access to content. Using the "Public access" feature, specific content items can be protected and made accessible only for authorized members. The same is possible in the Delivery API.
By default, protected content is ignored by the Delivery API, and is never exposed through any API endpoints. However, by enabling member authorization in the Delivery API, protected content can be accessed by means of access tokens.
Member authorization in the Delivery API was introduced in version 12.3.
Member authentication and authorization in the Delivery API is performed using the OpenId Connect flow Authorization Code Flow + Proof Key of Code Exchange (PKCE). This is a complex authorization flow, and it is beyond the scope of this article to explain it. Many articles can be found online that explain the flow in detail.
Member authorization is an opt-in feature of the Delivery API. To enable it, configure MemberAuthorization:AuthorizationCodeFlow
in the DeliveryApi
section of appsettings.json
:
Enabled
must be true
.
One or more LoginRedirectUrls
must be configured. These specify where the server is allowed to redirect the client after a successful authorization.
Optionally one or more LogoutRedirectUrls
must be configured. These specify where the server is allowed to redirect the client after successfully terminating a session.
These are only necessary if logout is implemented in the client.
All redirect URLs must be absolute and contain the full path to the expected resource. It is not possible to use wildcards or to allow all paths under a given domain.
When changing the MemberAuthorization
configuration, Umbraco must be restarted to pick up on the changes.
When enabling or disabling member authentication, the DeliveryApiContentIndex
must be rebuilt to correctly reflect the existing content protection state.
Many client libraries support automatic discovery of the server OpenId endpoints. This is also supported by the Delivery API, so likely we do not have to worry about the server endpoints.
If automatic discovery is not applicable, the server endpoints must be configured manually. The server endpoints can be found at https://{server-host}/.well-known/openid-configuration
.
Keep in mind that the API versions can change over time, which might affect the configuration.
To connect the client and the server, we need to apply some configuration details to the connection:
The client_id
must be umbraco-member
.
The response_type
must be code
.
The redirect_uri
must be one of the configured LoginRedirectUrls
.
The scope
must either be empty, or be openid
and/or offline_access
.
PKCE must be enabled.
Authorization Code Flow + Proof Key of Code Exchange (PKCE) requires the authentication service (identity provider) to be separate from the client application. This is to ensure that credentials are never exposed directly to the client application.
As an authentication service, we can use both Umbraco's built-in member authentication and external identity providers. By default the Delivery API attempts to use the built-in member authentication.
First and foremost we need a login page. By ASP.NET Core defaults, this page should be located at /Account/Login
. However, we can change the default path by adding the following piece of code:
To invoke this code, we need to call SetCustomMemberLoginPath()
in Program.cs
:
No matter the path to the login page, we still need a page to render the login screen. Create a content item located at the login page path, and use this template to render it:
With all this in place, it's time to test the setup. Use a browser to perform a request to https://{server-host}/umbraco/delivery/api/v1/security/member/authorize
with these query string parameters:
client_id=umbraco-member
redirect_uri=https://absolute.redirect.url/path/after/login
(replace the value with one of the configured login redirect URLs)
response_type=code
code_challenge=WZRHGrsBESr8wYFZ9sx0tPURuZgG2lmzyvWpwXPKz8U
code_challenge_method=S256
If everything works as expected, the request will yield a redirect to the login page. Completing the login form will cause a redirect to the specified redirect URL with a code
query string parameter. The code
can subsequently be exchanged for an access token, which can be used to access protected content.
Do not worry about the URL construction and subsequent handling of the code
parameter. This complexity is what the OpenId Connect client libraries handle for us.
The Delivery API supports the same functionality. In the following we'll be using GitHub to test this.
Once the App is created, generate a new client secret within the App. Make sure to copy both the client ID of your App and the generated secret.
Now we need to connect Umbraco members with the App:
Add the NuGet package AspNet.Security.OAuth.GitHub
to your Umbraco project.
Add the code below to configure the connection to the App. Remember to update the OAuth client ID and secret.
Finally, we need to invoke the connection configuration by calling AddGitHubAuthentication()
in Program.cs
.
There are multiple ways of registering extensions and dependencies like these in your Umbraco project. Which method to use depends on your implementation and preferred way of working.
Now we can test the setup. We'll be calling https://{server-host}/umbraco/delivery/api/v1/security/member/authorize
as described previously, but we need to add one more query string parameter:
identity_provider=UmbracoMembers.GitHub
If the setup is correct, the request will yield a redirect to the GitHub login page. Here we need to authorize the GitHub OAuth App we created earlier, in order to complete the login. Upon completion, a series of redirects will once more take us to the specified redirect URL with a code
query string parameter.
We can also add the external identity providers to the member authentication login screen. This way the end user can decide whether to log in as a registered member, or use an external identity provider.
When the authorization flow completes we'll obtain an access token. This token can be used as a bearer token to access protected content for the logged-in member:
Access tokens expire after one hour. Once expired, a new access token must be obtained to continue accessing protected content.
Refresh tokens provide a means to obtain a new access token without having to go through the authentication flow. A refresh token is issued automatically by the Delivery API when the offline_access
scope is specified in the authorization request.
Refresh tokens are subject to certain limitations and can result in security issues if not applied correctly. All this is beyond the scope of this article to explain in detail. Please familiarize yourself with the inner workings of refresh tokens before applying them in a solution.
The member authorization is tied to the access and refresh tokens obtained in the authorization flow. Discarding these tokens efficiently terminates the access to protected content.
However, the tokens are still valid and can be reapplied until they expire. Depending on your scenario, it might be prudent to revoke the tokens and maybe even terminate the session on the server.
Access and refresh tokens can be revoked by performing a POST
request containing the token:
When terminating a session on the server, the member is logged out of Umbraco. This means any subsequent authorization attempt will require an explicit login.
To terminate the active session for any given member, you must redirect the browser to the signout endpoint. The request must contain one of the white-listed LogoutRedirectUrls
from the appsettings.json
:
The Delivery API Swagger document can be configured to support member authentication.
Before we can do that, we need two things in place:
We must add https://{server-host}/umbraco/swagger/oauth2-redirect.html
to the configured LoginRedirectUrls
.
With these in place, we can enable member authentication in Swagger for the Delivery API by adding the following to Program.cs
:
The Swagger UI will now feature authorization.
Remember to use umbraco-member
as client_id
when authorizing. client_secret
can be omitted, as it is not used by the authorization flow.
The following samples show how to configure an ASP.NET Core client to utilize member authorization in the Delivery API.
To put these samples into context, please refer to the article above.
When using external identity providers, Umbraco still allows for performing local two-factor authentication for members. This feature is not available in the Delivery API. Instead, two-factor authentication should be performed at the identity provider.
Configuration key | Description |
---|---|
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
Response Status | Description |
---|---|
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
Response Status | Description |
---|---|
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
Response Status | Description |
---|---|
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
Response Status | Description |
---|---|
The Media Delivery API allows for accessing the Umbraco media items in a headless manner. This API applies many of the same concepts as its content counterpart, although with fewer options. If you haven't already, please familiarize yourself with the before reading this article.
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
Name | Type | Description |
---|
If you are not familiar with members in Umbraco, please read the article.
Most programming languages have OpenId Connect client libraries to handle the complexity for us. is a great example of such a library. In ASP.NET Core, OpenId Connect support is built into the framework.
The index can be rebuilt from the .
For inspiration, the at the end of this article shows how to configure an ASP.NET Core client.
For more inspiration on using the built-in member authentication, check the article. Here you will also learn how to create member sign-up functionality.
Umbraco allows adding external identity providers for both backoffice users and members. The process is documented in detail in the article.
First, we need to create an OAuth App in GitHub. This is done in the . Use https://{server-host}/umbraco/signin-github
as authorization callback URL in the App.
Learn more about this in the article.
Different client libraries have different ways of declaring the identity_provider
in the authorization request. The shows how to configure this in an ASP.NET Core client.
The features an implementation of this combined login experience.
We have to implement a login page .
PublicAccess
Determines whether the enabled Delivery API should be publicly accessible or if access should require an API key.
ApiKey
Specifies the API key needed to authorize access to the API when public access is disabled. This setting is also used to access draft content for preview.
DisallowedContentTypeAliases
Contains the aliases of the content types that should never be exposed through the Delivery API, regardless of any other configurations.
RichTextOutputAsJson
Enable outputting rich text content as JSON rather than the default HTML output. JSON can be a preferred format in many scenarios, not least because it supports the routing of internal links better than HTML does.
id*
String
GUID of the content item.
expand
String
Which properties to expand and therefore include in the output if they refer to another piece of content.
fields
String
Which properties to include in the response. All properties are included by default.
Accept-Language
String
Requested culture.
Api-Key
String
Access token.
Preview
Boolean
Whether draft content is requested.
Start-Item
String
URL segment or GUID of the root content item.
200: OK
Content item.
401: Unauthorized
Missing permissions after protection is set up.
404: Not Found
Content item not found.
path*
String
Path of the content item.
expand
String
Which properties to expand and therefore include in the output if they refer to another piece of content.
fields
String
Which properties to include in the response. All properties are included by default.
Accept-Language
String
Requested culture.
Api-Key
String
Access token.
Preview
Boolean
Whether draft content is requested.
Start-Item
String
URL segment or GUID of the root content item.
200: OK
Content item.
401: Unauthorized
Missing permissions after protection is set up.
404: Not Found
Content item not found.
id*
String Array
GUIDs of the content items.
expand
String
Which properties to expand in the response.
fields
String
Which properties to include in the response. All properties are included by default.
Accept-Language
String
Requested culture.
Api-Key
String
Access token.
Preview
Boolean
Whether draft content is requested.
Start-Item
String
URL segment or GUID of the root content item.
200: OK
Content item.
401: Unauthorized
Missing permissions after protection is set up.
fetch
String
Structural query string option (e.g. ancestors
, children
, descendants
).
filter
String Array
Filtering query string options (e.g. contentType
, name
, createDate
, updateDate
).
sort
String Array
Sorting query string options (e.g. createDate
, level
, name
, sortOrder
, updateDate
).
skip
Integer
Amount of items to skip.
take
Integer
Amount of items to take.
expand
String
Which properties to expand and therefore include in the output if they refer to another piece of content.
fields
String
Which properties to include in the response. All properties are included by default.
Accept-Language
String
Requested culture.
Api-Key
String
Access token.
Preview
Boolean
Whether draft content is requested.
Start-Item
String
URL segment or GUID of the root content item.
200: OK
Content item.
401: Unauthorized
Missing permissions after protection is set up.
404: Not Found
Content item not found.
id* | String | GUID of the media item |
expand | String | Which properties to expand in the response |
fields | String | Which properties to include in the response (by default all properties are included) |
Api-Key | String | Access token |
path* | String | Path of the media item. The path is composed by the names of any ancestor folders and the name of the media item itself, separated by
. |
expand | String | Which properties to expand in the response |
fields | String | Which properties to include in the response (by default all properties are included) |
Api-Key | String | Access token |
id* | String Array | GUIDs of the media items |
expand | String | Which properties to expand in the response |
fields | String | Which properties to include in the response (by default all properties are included) |
Api-Key | String | Access token |
fetch* | String | Structural query string option (e.g. Please note: The default API implementation only supports |
filter | String Array | Filtering query string options (e.g. |
sort | String Array | Sorting query string options (e.g. |
skip | Integer | Amount of items to skip |
take | Integer | Amount of items to take |
expand | String | Which properties to expand in the response |
fields | String | Which properties to include in the response (by default all properties are included) |
Api-Key | String | Access token |
Boosting Delivery API performance with output caching.
In many cases the Delivery API output is stateless, and it is not absolutely necessary to push content updates immediately. In these cases the API can greatly benefit from using output caching.
Output caching is an opt-in feature in the Delivery API. It can be configured individually for both the Content and Media Delivery APIs. When enabled, API outputs are cached on the server for each unique request, and re-used until the cache expires.
Under the hood, the Delivery API utilizes the built-in output caching middleware in ASP.NET Core to handle the cache.
Output caching is primarily designed to increase performance. While the Delivery API is performant on its own, output caching takes the performance to another level.
Another aspect to consider is the overall server load. Uncached requests require much more processing time than cached requests. Especially for high traffic sites, even a short-lived output cache makes a significant difference in the server load. This might result in a lesser need to scale instances, and thus a greener footprint for the site.
However, output caching does come with a few trade-offs:
The cache lives in memory by default. This means the site will consume additional memory to handle the cache.
The cache does not expire automatically when content changes. Therefore, editors will experience increased publishing time when making changes to existing content.
Requests made in preview mode are not subject to output caching.
Output caching can be a bad fit in some cases:
If editors require immediate publishing of content updates (see above).
When using personalization in the API output.
If a custom property editor requires re-rendering for every request (for example if a property value converter outputs the current time).
Output caching must be explicitly enabled by configuration. To enable it, add the OutputCache
section to the DeliveryApi
configuration in appsettings.json
:
The output cache duration (time-to-live) can be configured for the Content and Media Delivery APIs. In the example above, content output is cached for 15 minutes while media output is cached for an hour.
The default output caching mechanism is based on the individual server instance memory. When hosting in a load balanced environment, this likely will not work, as the memory cache isn't synchronized across instances.
Instead you'll need to use a distributed caching solution like Redis cache. Starting with .NET 8, Microsoft supports output caching with Redis cache as backing store. Read more here.
While output caching is a great way to boost performance, it should never be used as a band-aid to solve poor uncached performance. The Delivery API is generally performant without caching.
If you experience performance issues while querying the Delivery API, your first step should be to diagnose and fix the root cause. This could be any number of things, like:
Un-performant value converters.
Overly complex queries.
An inexpedient content architecture.
...or something else entirely.
Hiding such problems behind output caching should only ever be considered as a short-term solution. In the long run it will not be a sustainable fix.
Using property expansion and limiting to shape the Delivery API output
This article explains the mechanics of property expansion and limiting in depth. If you haven't already, please read the Getting started article first - in particular the "Concepts" section.
Property expansion and limiting applies only to select property editors. The following built-in property editors in Umbraco support expansion and limiting:
Picker editors
Content Picker
Media Picker
Media Picker (legacy)
Multinode Treepicker
Block-based editors
Block List
Block Grid
Rich Text Editor (with blocks)
When working with property expansion and limiting in API queries, there are two rules of thumb to keep in mind:
Expandable properties are not expanded by default. They must be expanded explicitly.
All properties are included in the API output by default. We can apply limiting to limit the included properties.
In the following examples, we will be querying a content tree with blog posts and blog authors:
All blog posts are located under a root content item called "Posts".
All authors are located under a root content item called "Authors".
The blog post content type (post
) contains two properties that support expansion and limiting:
author
: A content picker for picking the author of the post.
coverImage
: A media picker for picking an image for the post.
The author content type (author
) contains a single property that supports expansion and limiting:
picture
: A media picker for picking a picture of the author.
When fetching a blog post, the author
and coverImage
properties are returned in their un-expanded representation by default. This representation does not contain any property data; the properties
collections of author
and coverImage
are empty:
Request
Response
If we want to show the author's picture when rendering the blog post, we need to expand the author
property. By expanding the property, the author properties (including picture
) are included in the output. We can achieve this by appending the expand
parameter to our request.
The expand
parameter syntax is as follows:
expand=properties[propertyAlias1,propertyAlias2,propertyAlias3]
Within the properties
part of the expand
parameter we can list the aliases of the properties we wish to expand. If we want to expand all expandable properties, we can use the operator $all
instead:
expand=properties[$all]
Request
Response
Now we have the picture
data in the properties
collection of author
. However, the rest of the author's properties (biography
and dateOfBirth
) are also present in our output, so we are slightly over-fetching. We will take care of that later.
First, we need to get the alt texts of our images (the blog post coverImage
and the author picture
). The alt text in this case is a text string property (altText
) on the media type. Fetching the alt texts is possible because property expansion can be performed both across multiple properties and in a nested fashion.
For nested property expansion, the expand
parameter syntax is as follows:
expand=properties[propertyAlias[properties[nestedPropertyAlias1,nestedPropertyAlias2]]]
Nested property expansion can also be combined with the $all
operator:
expand=properties[$all[properties[nestedPropertyAlias1,nestedPropertyAlias2]]]
There is no API limit to how "deep" the nesting can go. Eventually though, the total length of the request URL may become a hard limit to the size of the query.
Let's amend the expand
parameter to accommodate expansion of the images:
Request
Response
As mentioned above we are slightly over-fetching. We don't need all the author data - we are only interested in the author's picture
. To fix this we can apply property limiting by adding the fields
parameter to our request.
The fields
parameter allows us to limit the properties in the output to only those specified. The parameter uses the same syntax as the expand
parameter.
Our ideal blog post output contains:
All the properties of the blog post, including the altText
of the post coverImage
.
Only the picture
property of author
, including the altText
of the author picture
.
As with property expansion, we can use the $all
operator in the fields
parameter. This will include everything at any given query level. We'll use this to include all the blog post properties in the output without having to specify each property explicitly:
Request
Response
Now the API output contains only the properties we need to render the blog post.
Property limiting is particularly useful when querying multiple items. For example, if we were building a condensed list of blog posts, we likely wouldn't need the author data nor the blog post content. By applying limiting to a filtered query, we can tailor the API output specifically to this scenario:
Request
Response
If you are not familiar with block-based editors, please refer to this article for the general concepts of these.
In the API output, a block has little value without its contained properties. Therefore, the content and settings properties of blocks are always included in the output. However, these properties are not expanded. As such, we can apply expansion and limiting to the contained properties.
In the following examples we'll request different types of articles, all of which are located under a root content item called "Articles":
An article with a Block List property (blockList
).
An article with a Block Grid property (blockGrid
).
An article with a Rich Text Editor property (richText
).
All these properties are configured with a "Featured Post" block which consists of:
A content model (featuredPost
) that contains:
title
: A text string property.
post
: A content picker property that allows for picking a blog post.
A settings model (featuredPostSettings
) that contains:
backgroundColor
: An approved color property.
showTags
: A toggle property.
The goal is once again to build a condensed list of blog posts. But this time we'll build the list from the "Featured Post" blocks within each block editor.
To build the list we need the block title
, the coverImage
and excerpt
from the picked post, and the backgroundColor
from the block settings. Thus, we need to:
Expand the post
property to retrieve the altText
of the post coverImage
.
Limit both the block-level properties and the nested post
properties, as to only output the properties relevant for building the condensed list.
For comparison, the samples show both the default output and the output with expansion and limiting applied. Notice that:
The expand
and fields
parameter syntax is the same for all editors, even though their rendered output is structurally different.
The expand
and fields
parameters target both the content and settings parts of each block.
Default output without expansion and limiting:
Request
Response
Output with property expansion and limiting:
Request
Response
Default output without expansion and limiting:
Request
Response
Output with property expansion and limiting:
Request
Response
Request
Response
Output with property expansion and limiting:
Request
Response
Property expansion and limiting is a powerful feature that can boost our application performance. With this, we can prevent additional requests to obtain data for linked items, and we can tailor the output to specific use cases.
However, it is also a complex feature. The query syntax quickly gets complicated, particularly when targeting block editors. You will likely need to experiment to get the query exactly right. Hopefully, the examples in this article will guide you in applying expansion and limiting to your own content.
Configure custom preview URLs to provide editors with seamless access to external preview environments for the Content Delivery API data.
With Umbraco, you can save and preview draft content before going live. The preview feature allows you to visualize how a page will look once it is published, directly from within the backoffice. This is also possible for the Content Delivery API data. You can extend the preview functionality in the backoffice by configuring external preview URLs for client libraries consuming the Content Delivery API.
To get familiar with the preview functionality in the Delivery API, please refer to the Preview concept section.
The support for configuring additional preview environments in the Delivery API was introduced in version 12.3.
If your client libraries feature preview functionality, you can enable editors in Umbraco to navigate directly to their preferred preview environments. To achieve this, start by generating the necessary URLs for each environment you wish to allow for preview. These URLs need to trigger preview mode within your application, which will fetch and present draft content from the Delivery API.
Once you have these preview URLs, you will need to register them through code in Umbraco.
Additionally, there are plans to simplify this process further. In an upcoming major version of Umbraco, a UI will be introduced, allowing you to configure these custom preview URLs directly from the backoffice.
Here is an example of how to register such preview URLs for both variant and invariant content using a notification handler:
The purpose of this notification handler is to dynamically generate additional preview URLs for published content items only (for the sake of simplicity). It constructs two custom preview URLs, one for a development environment and another for a staging environment. These URLs include the content's route, culture variant, and a preview
query parameter to enable preview mode in the client application.
You can then register your notification handler in a composer like this:
Now that we have set up additional preview URLs for the Delivery API data, you can access them from the Content section. When you open a content node, you will see new preview options for the external environments you have configured. Next to the regular "Save and preview" button, there is an arrow for the multiple URLs that have been added. Click it to see all the available preview URLs, as shown below:
Below is an example with variants, showcasing both the English and Danish versions of a content node.