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