How can I include data from a parent node in the content delivery API

For a client project I am currently working on I have a structure as follows

  • Hotel (folder)

    • List item

(Below) Hotel Data (detail pages)

When I select Hotel Data I want it to include some parent info as well. As far as I can see the Content delivery API doesn’t support that? Would be great for a next release to make it possible to expand parent properties?

Is it really not possible or did I miss some docs?

As far as I know this is not possible. Usually when I need data from multiple nodes and combine data from multiple sources, I would create a custom endpoint. Maybe it’s a bit over the top in this case because it’s just the parent, but a custom endpoint allows you to tailor the data exactly to what you need. But it is more work, agreed…

1 Like

Yeah this is not supported.

You could look at the path that’s being returned in the first call (for the child) and do a second get of the parent based on the path. Would be a good feature request though!

1 Like

Hi Richard,

This is actually possible, what you can do is to extend the response from the Content Delivery API.

Here is an example on how to do it.

Mostly the code is boilerplate and is needed to extend the response but the magic happens where I have fetched the parent content and added a custom extra object to the response.

public class MyDeliveryApiJsonTypeResolver : DeliveryApiJsonTypeResolver
{
    protected override Type[] GetDerivedTypes(JsonTypeInfo jsonTypeInfo)
        => jsonTypeInfo.Type == typeof(IApiContentResponse)
            ? new[] { typeof(MyApiContentResponse), typeof(ApiContentResponse) }
            : base.GetDerivedTypes(jsonTypeInfo);
}

public class MyApiContentResponseComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.Services.AddSingleton<IApiContentResponseBuilder, MyApiContentResponseBuilder>();
        builder
            .Services
            .AddControllers()
            .AddJsonOptions(
                Constants.JsonOptionsNames.DeliveryApi,
                options => options.JsonSerializerOptions.TypeInfoResolver = new MyDeliveryApiJsonTypeResolver());
    }
}

public class MyApiContentResponseBuilder : ApiContentResponseBuilder
{
    private readonly IHttpContextAccessor _httpContextAccessor;


    public MyApiContentResponseBuilder(
        IApiContentNameProvider apiContentNameProvider,
        IApiContentRouteBuilder apiContentRouteBuilder,
        IOutputExpansionStrategyAccessor outputExpansionStrategyAccessor,
        IHttpContextAccessor httpContextAccessor)
        : base(apiContentNameProvider, apiContentRouteBuilder, outputExpansionStrategyAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override IApiContentResponse Create(
        IPublishedContent content,
        string name,
        IApiContentRoute route,
        IDictionary<string, object?> properties)
    {

        var parentCustomData = new
        {
            ParentId = content.Parent?.Key,
            ParentName = content.Parent?.Name,
            ParentType = content.Parent?.ContentType.Alias
        };

        IDictionary<string, IApiContentRoute> cultures = GetCultures(content);

        return new MyApiContentResponse(
            content.Key,
            name,
            content.ContentType.Alias,
            content.CreateDate,
            content.UpdateDate,
            route,
            properties,
            cultures,
            parentCustomData);
    }
}

public class MyApiContentResponse : ApiContentResponse
{
    public object ExtraData { get; }

    public MyApiContentResponse(
        Guid id,
        string name,
        string contentType,
        DateTime createDate,
        DateTime updateDate,
        IApiContentRoute route,
        IDictionary<string, object?> properties,
        IDictionary<string, IApiContentRoute> cultures,
        object extraData)
        : base(id, name, contentType, createDate, updateDate, route, properties, cultures)
        => ExtraData = extraData;
}

Let me know if you need more help.

3 Likes

Thanks that would indeed help if I needed it always. I ended up creating a property that returns al the parent properties and works for now. Thanks for your help.