Sitecore JSS: Extending the Layout Service

Featured image

JSS (JavaScript Services) is Sitecore's answer to the need of having a decoupled CMS. One of the reasons for the popularity of the headless architecture is because you can redesign your CMS without setting up a new implementation of the CMS itself. A second reason is that developers do not need to know anything about the backend system. JSS is highly flexible and out of the box it supports a a lot when it comes to customization. Nevertheless, we sometimes need to extend the reality as we see it  ;-)

example output of Sitecore JSS Layout Service

Customization areas

JSS is highly flexible and has a lot of options to get your data from the service endpoint. And if you want to do even more you can get it through GraphQL out of the box by configuring a query on the rendering. And if that isn't enough you can extend the layout service. Extending can be done in the following areas:

In this post I will show you how easy it is to change the rendering contents.

Customizing the Layout Service Rendering Output

There are three ways of changing the output of the service to your needs. Two ways can be done by configuration and is done with ease through the content editor:

Configuring the built in Rendering GraphQL support - If you are new to GraphQL make sure to check out my post Sitecore JSS: Create your own GraphQL schema. GraphQL is the new way of querying an API with a JSON-payload. Some like REST, but much more flexible and maintainable. In regard to configuring the queries two tastes can be chosen here: connected mode or integrated mode. With the connected mode data is passed through the GraphQL query from the client code. The layout service will not handle this request and you surely have to take care that you've selected. Using the integrated mode the data is returned through the layout service.

Example of integrated mode

Configuring a builtin Rendering Contents Resolver - The content resolvers are a builtin mechanism which allows you to configure a rendering in that way that the data returned matches the configuration. When you install JSS, you will get five resolvers out of the box:

The Item Selector Query is a very powerful mechanism but use it with care because it can influence the performance

contentresolvers

Create a RenderingContentsResolver - And because I love programming this is my favorite party_parrot!! If any of the out of the box solutions does not fit your needs, you must code it yourself. And this time I want to change the way the data is serialized and returned from the service. Fist, we are going to create a C# resolver class. There are two options to do so. You can choose to implement the IRenderingContentsResolver interface and build the implement the logic yourself or inherit from the RenderingContentsResolver class. The second one is great as it has some methods to override which are usefull when you want to resolve items from elsewhere in the content tree.

Then we have to create the Content Resolver item and bind it to the class and the assembly name. We leave use context item unchecked because we want to use our datasource and even extend the template.

customitem

Now we need to create the custom code. No extra configuration in Sitecore is needed to be able to use this code. So its sufficient when the assembly resides in the bin-folder.

And then the code:


using Newtonsoft.Json.Linq;
using Sitecore.Data.Items;
using Sitecore.LayoutService.Configuration;
using Sitecore.LayoutService.ItemRendering.ContentsResolvers;
using Sitecore.Mvc.Presentation;
using System.Collections.Generic;
using Foundation.Macaw.Dictionary.Helpers;
using Foundation.Macaw.Items.Interfaces;
using Foundation.Macaw.Modeling.Helpers;
using Foundation.Macaw.Modeling.Interfaces;
using Foundation.Macaw.Modeling.Models.ViewModels;
using System.Web.Mvc;

namespace Macaw.Sitecore.LayoutService.ItemRendering.ContentsResolvers
{
    public class CustomRenderingContentsResolver : RenderingContentsResolver
    {
        protected readonly IItemService _itemService;
        protected readonly IFeatureModelService _featureModelService;

        public CustomRenderingContentsResolver()
        {
            _itemService = DependencyResolver.Current.GetService(); 
            _featureModelService = DependencyResolver.Current.GetService(); 
        }

        public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
        {
            var datasource = !string.IsNullOrEmpty(rendering.DataSource)
                ? rendering.RenderingItem?.Database.GetItem(rendering.DataSource)
                : null;

            var boilerplate = new {
                    datasource.ID,
                    datasource.Name,
                    Datasource = GetItemViewModel(),
                    Parameters = RenderingParametersModelHelper.GetRenderingParameters(), 
                    Dictionary = DictionaryItemsHelper.GetRenderingDictionaryItems(), 
                };

            object json =  base.ResolveContents(rendering, renderingConfig);

            return new { fromContentResolver = json, macaw = boilerplate };
        }

        private ItemViewModel GetItemViewModel()
        {
            var item = _itemService.GetLayoutItem();
            return ViewModelHelper.GetItemViewModel(item);
        }

    }
}
</code>
</pre>

Just drop the dll in the bin folder and make sure the binding in the resolver item in the content editor matches. Then let's move to the rendering and select our created item:

select

Secondly, we want to change our template to give it some more data:

template

Now let's fill in the fields of the item we had:

item

And finally, let's request our data from the API:

zzz

From the code you can see that I output an anonymous type holding two properties. The first one is the resolved data by JSS (fromContentResolver) and the second part is the contents that are resolved by the custom logic (macaw). There are two things that stand out right away when you look at the field structure returned from the layout service. The data in the fromContentResolver part hold all field values in a list having its name and a value property which exposes the value. In addition, it also got the data from the rest of the resolved items from my custom logic. The custom logic, however, holds business logic like inheritance of field values. This means that when the SEO title is blank it will take the title of the parent.