Showing posts with label jss. Show all posts
Showing posts with label jss. Show all posts

Thursday, July 3, 2025

XP editor navigation issues in Sitecore JSS

Experience Editor & Sitecore JSS

As you probably know, Sitecore's experience editor does work on headless sites created with Sitecore SXA and JSS enabling editors to work in headless sites just as they would in the non-headless ones.  We had such a headless site and bumped into a few issues in the experience editor though. 

An important note here is that this headless site is not the only one on this Sitecore instance (10.2) - there are others, non-headless and non-sxa mvc sites. 

Navigation bar

Our first issue was the navigation bar. The navigation bar allows you to navigate to a specific page through a menu structure, a bit like a breadcrumb.  To enable the navigation bar, in the ribbon, on the View tab, select Navigation bar.


Our issue was that the urls in that navigation were wrong - they were using the wrong sc_site parameter in the querystring. 

SiteResolving

First thing we had to check was the configuration of the sites. When adding sites with config patches they can be ordered. We had done this properly, but the headless site is a SXA site and that does not gets added through a config file. However, there also is a config section siteResolving in the ExperienceAccelerator section. This needs to include all your non-sxa sites so you need to patch them in.
<siteResolving patch:source="Sitecore.XA.SitesToResolveAfterSxa.config">
  <site name="exm" resolve="after"/>
  <site name="website" resolve="after"/>
  <site name="unicorn" resolve="after"/>
  <site name="x" resolve="after" patch:source="xxx.Sites.config"/>
  <site name="y" resolve="after" patch:source="xxx.Sites.config"/>
  <site name="z" resolve="after" patch:source="xxx.Sites.config"/>
</siteResolving>
This is a good and necessary step forward, but unfortunately it did not solve our problem yet. 

TargetHostName

The final step to get it working was adding a target hostname to the site definition of our headless sxa-jss site. Not an ideal solution, but it does work. 

Ok, issue number one solved - the navigation bar is working.




LinkFields

We also noticed we had a problem when using LinkFields. 

When we have a RT field in the XP editor, the links inside are transformed into something like https://.../~/link.aspx?_id=5F7338F6FCFC404EB1EF67FAE1F71F7D&_z=z. In components using integrated GraphQL to fetch data however, we notice that the LinkFields are not transformed in the same way (on the same page). We fetch the jsonValue of the fields, use the Link component from the jss sdk in our Next.js code. This work perfectly in normal mode, but in the XP editor we get links like https://.../en/Test%3Fsc_site=X. If editors click on those links they get an error. 

I asked this on Sitecore StackExchange but didn't get an answer there. So I opened a Sitecore support ticket and after a few rounds with questions and answers and logs, videos and data gathering we finally came to a solution. 

But before we come to the solution, let's take a step back to see how we got there. 

Step 1 - the ?

First thing we noticed was the %3F instead of a ? to start the querystring. Apparently there is a bug called "Internal link query string encoded in Next.js app" but we actually just fixed it in the Next app itself by replacing the character. Problem 1 solved.

Step 2 - sc_site

To get the correct site in the links I first wanted to check the data that came from the graphQL queries. To do this, I wanted to use the grapQL edge UI in edit mode and apparently that is possible. How to do that is documented on StackExchange - https://sitecore.stackexchange.com/a/39193/237 - so no need to repeat it all here. In short, if you want to fetch the data in edit mode:
  1. Fetch a token from the Identity Server using Postman (or similar tool) as explained here
  2. Add the authorization header in the http headers
  3. Add sc_mode=edit to your querystring



This way we learned that the url was definitely coming from Sitecore the wrong way. So back to Sitecore... 

Maybe now is a good time to mention that although this headless site is rather new, the other mvc sites on the same platform are not. Instead, it's an old spaghetti platform that we inherited - I guess if you have been around in this business for a while you know what I mean.

And so, eventually we (to be honest, Sitecore support) found that someone patched a setting Languages.AlwaysStripLanguage to false. 

The default value for this setting is true, and it specifies if the StripLanguage processor in the "preprocessRequest" pipeline will parse and remove languages from the url, even when the languageEmbedding attribute of the linkProvider is set to "never".  

Changing this setting back to the original value (true) fixed our issue. And didn't cause any new ones... 


Conclusion

Don't touch settings that you should not touch... 

Friday, October 4, 2024

Introducing headless in a Sitecore MVC environment

 An old Sitecore site

A while ago we inherited a customer that had been with Sitecore for many years. They had 2 sites - a main corporate one and a smaller dedicated one - running on an old Sitecore version. The maintenance was horrible, deploying a nightmare and therefor costs were going through the roof. 

Sounds familiar? As a Sitecore developer I bet it does, as this will definitely not be the only solution like that out there.

After a cleanup and some upgrading we had at least again a stable environment and together with the customer decided to re-do the smaller site. A bit as a test to see what the real and current capabilities of the platform are.  A headless architecture was designed for this site - so the result would be to have one (old) mvc site and a headless site in the same Sitecore instance.  Sounds like a great way to introduce customers to headless architectures and what this all means for them without the need for an immediate big bang. 

I know there are already options to "upgrade" or transfer your site to headless setups, but even that approach can cost quite a bit of money and my idea has been that if you do this kind of investment and architectural change you might as well re-evaluate what your site is all about. 

First steps to headless

We introduced the customer to headless. While talking to them about the functionality and design - as this is indeed a moment to really think about those - we also informed them about the changes to hosting and deployment and what this means for them. The fact that we can deploy parts of the solution completely independent is a big bonus and maintenance in general is much more flexible now. 

This all sounds great, but how to start practically - and will this all work?

Practical steps

Our first step was to install SXA on our test environment to see if the other (mvc) site still works when that is installed. In the early days of sxa that used to be a problem (or at least was very likely to) but now we didn't detect any issues. So we could move forward and installed JSS. This will not interfere with the other sites as they are not using the headless services. The main challenge here was to get the correct version of every component needed here (ok, that could be our own fault...).

We use NextJS for the front end application, and I remember we had to do our setup of this part a few times because some things didn't work and in the end it seemed that somehow somewhere we did take a wrong version of something.. so be cautious here. But once the starter template was there, the team was ready to start.

How it is going

Very well, thank you. The headless site is live, and the customer is starting to see the benefits of the new abilities. We can deliver faster, deploy easier, and the editors are still working in the Sitecore environment they are used to. Although, not sure if that last part is a benefit as those editors might be a bit outdated compared to what is on the market today. 

Conclusion

We took a first step with this customer without too much hassle. We do hope this is a first step towards more of course - where the end goal is a total move towards a completely headless cloud based solution. But when that (big) step is too much too take at once, there are solutions. Plural - as there are a few ways to tackle it, this just being one of them.  But with an open mind and clear an honest communication with a client, anything can be done.
 

Friday, September 13, 2024

Sitecore JSS hiding data in props

 Sitecore JSS props

A small extension to my post on Sitecore JSS and extranet security which I wrote in July. If you haven't read that one, please do so first as I will try not to repeat too much here.

What is important to mention is that we are sending roles that are allowed to see the pages along with the page data. These roles are selected in a multiselect field.  This way the front application is in charge of the security as it should. 

We noticed a problem however when we looked at the source code of the generated pages. They were listing our role id's and names. This is of course information that we would rather not show to everyone. 

Hiding page data

So our problem is data from the page that we would like to use, but not show to users.  In our case it was security related, but that could be any data of course.

We could try to change the output and adapt the way a MultilistField sends its output. I would assume the GetMultilistFieldSerializer or MultilistFieldTypeFactory might be a good start, but actually I don't want to change that behavior just for 1 field. It seems like a messy solution, especially if we would get more data like this. As it is the front app that is putting this data in it's output, it is that app's responsibility to keep it hidden.

A MultilistField will by default show the id, the url, the name, the display name and the fields of the selected items. But even if we would just have the raw value -which is the minimal that we need for our functionality- we should hide that from public eyes.

I want to mention again that I am nowhere near a NextJs expert so for me this is gibberish but my front-end companion came up with a function that is now used to filter out the "roles" field:
import { SitecorePageProps } from 'lib/page-props';

export const whiteListProps = async (props: SitecorePageProps) => {
  delete props.layoutData.sitecore?.route?.fields?.roles;
  return props;
};
In the [[...path]].tsx this function is used on the props.


As a non-expert on the matter, I was surprised to see that all data coming from Sitecore was visible in a browser. Assuming there are more non-experts out there, I hope this post makes some sense and might keep some sensitive data hidden.

Friday, July 5, 2024

Sitecore JSS extranet security

 Extranet with Sitecore JSS and NextJS


Creating an extranet in a headless setup isn't always that straight forward. It is not up to your content management system (in this case Sitecore) anymore to deliver pages and as such also not to check who is actually demanding which content.  The Sitecore security settings as we used in the XP days are not sufficient here.

In this case we also had a few particular requirements and architectural choices:
  • users are not stored through Sitecore, but in a custom database
  • roles are maintained in Sitecore (as content items)
  • pages can be visible for people from one or more roles, or even for people without a role (not yet registered guests)
  • components can have security settings that define which role(s) can view the component
  • API calls are also using the roles as security guidelines to determine who can get which data

Let's focus on the parts that get us in touch with Sitecore. So we need a solution that can set security on pages and components for roles defined and known in Sitecore. 

Pages

We created a base template with 2 fields:
  • a multiselect field to select one or multiple roles
  • a checkbox field to set the page open for everyone - including guest without a role
As we do not want to close pages for everyone (would be silly - one could unpublish the page if you want that) we also count no selected roles as all roles. 

This base template is added to all page templates. As this data is part of the page data, it is included when a page is requested by the NextJs app and can be used by that app to handle the security appropriately - send a 403 page if the page is not available for the current user.

I'm afraid I cannot share code in this article so you'll have to do with the ideas. But if you are familiar with NextJs development (which I am not btw) I would assume that this is not rocket science.

A small tip however: do not forget to check editing mode, as you do not want the security rules to be adapted when editing. 

Components

For the components, we used a similar setup. We created another base template that can be included in a component - it will not be included in all components, just in those where we want the security functionality.  This creates also a similar setup in the front app as that will receive the security data and display the components based on what it knows about the current user. 

Again, don't forget to skip the security check when editing. Although it can look weird when you have multiple components targeted at different roles, you still want to be able to edit everything.


Options

I assume there would be other options to tackle our request. It feels a lot like personalization so we also thought in that direction, but in the end our solution seemed like a simple one that would do as expected. And it is - the implementation was fairly easy and fast and the editors find their way when editing the pages and components. 

As I could only share the conceptual ideas here I hope you found it useful. And if you have any other thoughts or ideas on how to handle such requirements, I would be happy to discuss those. Always eager to learn ;) 


Thursday, May 23, 2024

Sitecore JSS Dictionary performance

 Sitecore JSS Dictionary performance tuning


We implemented a headless site with NextJS and a Sitecore 10.2 with SXA - headless SXA. The code generated by Sitecore when creating the project included all we needed to work with the SXA dictionary in a dictionary-service-factory.ts. In combination with the i18n setup this allows us to easily create and use translatable labels in our application. It works fine. 

But.. would someone please take a look at the logs...

Always watch the search logs

After a while we checked the logs to see if anything fishy was found - always a good practice, even during development cycles and when you think everything is smooth. And we noticed a lot of identical queries:

INFO  Solr Query - ?q=(((path6d119245ea284770a401ee68458abfc5) AND _language"en")) AND _templates6d1cd89719364a3aa511289a94c2a7b1)) AND _path0de95ae441ab4d019eb067441b7c2450)) AND _val:_boost&start=30&rows=10&fl=*,score&fq=_indexname:(sitecore_web_index)&wt=xml&sort=_smallcreateddate_tdt desc,_group asc

It didn't ring a bell immediately but when we executed this query in the solr admin it was very clear that this was related to the dictionary. Apparently our next app was fetching the dictionary data quite often. And in small pieces. The combination of both settings leads to a lot of queries. Even though they are probably very fast, I still thought it could be improved as the labels in our dictionary will not change frequently. 


Tweaking the DictionaryService

In the dictionary-service-factory.ts file, check for the GraphQLDictionaryService instance creation (we are using the GraphQL version):
new GraphQLDictionaryService({
      endpoint: config.graphQLEndpoint,
      apiKey: config.sitecoreApiKey,
      siteName: config.jssAppName,
      ...

Looking at the options available for this service, we found we could set a pageSize and a cacheTimeout. There is also a parameter cacheEnabled but that is true by default.  Note that the cacheTimeout has a default of 60 (seconds) and the pageSize has a default of 10 (as seen in the rows parameter in the solr query). 

For our implementation and with the number of dictionary items we have that is inefficient. We need a larger pageSize and a larger timeout. 

  new GraphQLDictionaryService({
          endpoint: config.graphQLEndpoint,
          apiKey: config.sitecoreApiKey,
          siteName: config.jssAppName,
          rootItemId: '....',
          pageSize: 150,
          cacheEnabled: true,
          cacheTimeout: Number(process.env.DICTIONARY_CACHE_TIMEOUT),

With this adjustments our dictionary was still working fine - but we had a lot less queries to solr. Note that we decided to get the timeout from an environment variable so we could tweak that between environments. You could do the same for the page if you want.


Enjoy the tips and happy dictionaring 🙂 

Tuesday, July 5, 2022

Extending the Edge endpoint in Sitecore JSS

Sitecore JSS & the Edge endpoint

I was working on a project with Sitecore JSS, NextJS and the Edge endpoint - which is the default when you use the starter template from Sitecore and apparently the way to go now. If you read some of my previous you might know we had some issues getting it all to work but in the end it all worked like a charm. 

But we are used to tweak/improve Sitecore to our specific needs...  so I wanted to add a custom field (the tokened treelist as described before). If I now query items with such a field I get those values as strings - the field is not handled like a normal TreeList. I read in the documentation that extending the schema is not supported as it would break compatibility with Experience Edge - so that would mean we cannot use this custom field. So I was curious and wanted to try anyway.

I found 2 options to get this done. Well, to be honest.. one that actually worked and one that should work but I didn't have enough time to investigate fully. 

Let's start with that last one.

SchemaExtender

A SchemaExtender should be able to add or modify types to the completed schema. There is some documentation on the Sitecore docs but it's rather limited. I managed to get some code working to extend the options for an image field. However, in the (limited) time I had I couldn't get the extra custom TreeList to work. So if anyone knows how to do this, I would really appreciate a blogpost. Maybe I'll add this question to Sitecore StackExchange as well.

I can share the code to extend the images though:

public class ImageSchemaExtender : Sitecore.Services.GraphQL.Schemas.SchemaExtender
{
  public ImageSchemaExtender()
  {
    ExtendType<Sitecore.Services.GraphQL.EdgeSchema.GraphTypes.FieldTypes.ImageFieldGraphType>("ImageField", type =>
    {
      ExtendField(type, "src", field =>
      {
        field.Arguments.Add(new QueryArgument<BooleanGraphType>
        {
          Name = "ratio",
          Description = "Ignore ratio"
        });
        
        field.Resolver = new FuncFieldResolver<Field, object>(context => GetUrl(context.Source, context.Arguments));
      });
    });
  }

  protected string GetUrl(ImageField field, Dictionary<string, object> arguments)
  {
    var mediaItem = field.MediaItem;
    if (mediaItem == null)
      return (string)null;
    var options = new MediaUrlBuilderOptions();
    if (arguments.TryGetValue("maxWidth", out var obj1) && int.TryParse(obj1?.ToString(), out var result1))
      options.MaxWidth = result1;
    if (arguments.TryGetValue("maxHeight", out var obj2) && int.TryParse(obj2?.ToString(), out var result2))
      options.MaxHeight = result2;
    if (arguments.TryGetValue("ratio", out var obj3) && bool.TryParse(obj3?.ToString(), out var result3))
      options.IgnoreAspectRatio = result3;
    return MediaManager.GetMediaUrl(mediaItem, options);
  }
}
<sitecore>
  <api>
    <GraphQL>
      <endpoints>
        <edge>
          <extenders hint="list:AddExtender">
            <imageExtender type="Project.Platform.Foundation.Images.ImageSchemaExtender, Project.Platform" />
          </extenders>
        </edge>
      </endpoints>
    </GraphQL>
  </api>
</sitecore>
You can see here that we can extend the ImageFieldGraphType. As mentioned - if anyone reading this knows how to create such an extender that would extend the schema in a way that a custom TreeList field works just like the ootb one I would love to read that solution 😉


FieldTypeFactoryStore

Other endpoints seem to have options to plug your code in through config so I tried to find out where the Edge endpoint defines how a field should be mapped to a piece of code. And I found that part in a class called FieldTypeFactoryStore. 

Problem with this class is that it is clearly not meant to be extended. As I'm not sure if adding custom fields is actually supported this might make sense. But just for being curious I tried and it can work. Create your own version of this class and register it.. that can do it. Although I believe it is not a good idea.

using System.Collections.Generic;
using Sitecore.Services.GraphQL.Content.TemplateGeneration.FieldMapping;
using Sitecore.Services.GraphQL.EdgeSchema.GraphTypes.FieldTypes;
using Sitecore.Services.GraphQL.EdgeSchema.TemplateGeneration.FieldMapping;

namespace Project.Platform.Foundation.Images
{
  public class FieldTypeFactoryStore : IFieldTypeFactoryStore
  {
    private readonly Dictionary<string, IFieldTypeFactory> fieldTypes;

    public FieldTypeFactoryStore()
    {
      fieldTypes = new Dictionary<string, IFieldTypeFactory>();
      fieldTypes.Add("Checkbox", new GenericFieldTypeFactory<CheckboxFieldGraphType>());
      fieldTypes.Add("Date", new GenericFieldTypeFactory<DateFieldGraphType>());
      fieldTypes.Add("Datetime", new GenericFieldTypeFactory<DateFieldGraphType>());
      fieldTypes.Add("Image", (IFieldTypeFactory)new GenericFieldTypeFactory<ImageFieldGraphType>());
      fieldTypes.Add("Integer", new GenericFieldTypeFactory<IntegerFieldGraphType>());
      fieldTypes.Add("Number", new GenericFieldTypeFactory<NumberFieldGraphType>());
      fieldTypes.Add("Checklist", new GenericFieldTypeFactory<MultilistFieldGraphType>());
      fieldTypes.Add("Multilist", new GenericFieldTypeFactory<MultilistFieldGraphType>());
      fieldTypes.Add("Multilist with Search", new GenericFieldTypeFactory<MultilistFieldGraphType>());
      fieldTypes.Add("Name Value List", new GenericFieldTypeFactory<NameValueListFieldGraphType>());
      fieldTypes.Add("Name Lookup Value List", new GenericFieldTypeFactory<NameValueListFieldGraphType>());
      fieldTypes.Add("Treelist", new GenericFieldTypeFactory<MultilistFieldGraphType>());
      fieldTypes.Add("TreelistEx", new GenericFieldTypeFactory<MultilistFieldGraphType>());
      fieldTypes.Add("Droplink", new GenericFieldTypeFactory<LookupFieldGraphType>());
      fieldTypes.Add("Droptree", new GenericFieldTypeFactory<LookupFieldGraphType>());
      fieldTypes.Add("General Link", new GenericFieldTypeFactory<LinkFieldGraphType>());
      fieldTypes.Add("General Link with Search", new GenericFieldTypeFactory<LinkFieldGraphType>());
      fieldTypes.Add("Rich Text", new GenericFieldTypeFactory<RichTextFieldGraphType>());
      fieldTypes.Add("lookup", new GenericFieldTypeFactory<LookupFieldGraphType>());
      fieldTypes.Add("reference", new GenericFieldTypeFactory<LookupFieldGraphType>());
      fieldTypes.Add("tree", new GenericFieldTypeFactory<LookupFieldGraphType>());
      fieldTypes.Add("default", new GenericFieldTypeFactory<ItemFieldGraphType>());
      ....
    }

    public IFieldTypeFactory GetFieldFactory(string fieldType)
    {
      fieldTypes.TryGetValue(fieldType, out var fieldTypeFactory);
      return fieldTypeFactory;
    }

    public IFieldTypeFactory Default => fieldTypes["default"];
  }
}
<sitecore>
  <services>
    <register serviceType="Sitecore.Services.GraphQL.EdgeSchema.TemplateGeneration.FieldMapping.IFieldTypeFactoryStore, Sitecore.Services.GraphQL.EdgeSchema" implementationType="Sitecore.Services.GraphQL.EdgeSchema.TemplateGeneration.FieldMapping.DefaultFieldTypeFactoryStore, Sitecore.Services.GraphQL.EdgeSchema">
	  <patch:attribute name="implementationType">Project.Platform.Foundation.Images.FieldTypeFactoryStore,Project.Platform</patch:attribute>
    </register>
  </services>
</sitecore>

So..  I'm afraid I didn't really get it working - yet.  But sharing it anyway as we/you can also learn from attempts that were not completely successful. If anything changes I'll keep you posted. I anyone who reads this has more information, please keep me posted as well...

Future - the cloud

What this means in the future with XM Cloud..  I don't know. I was told it should be possible so maybe it will. Or maybe it won't. It makes sense that you can't change how the Edge works - but as I don't know the details about it, it's not (yet) clear what will be possible and what not. Room for more blog posts 🙂


Friday, March 18, 2022

Sitecore Managed Cloud with SXA and JSS

 Sitecore Managed Cloud with JSS and NextJS

First of all: thanks to David Szöke who recently also wrote an article on Sitecore Managed Cloud saving me a lot of time as I now have to write a lot less - thanks for sharing David! 👍
And if you haven't read it yet, you should.

Before we continue, one small step back: if you don't know what Managed Cloud is and you need to get acquainted the documentation is (like always) a good start. 
Sitecore Managed Cloud is a hosting service that enables customers to deploy Sitecore Experience Manager (XM), Sitecore Experience Platform (XP), and Sitecore Experience Commerce (XC) on Microsoft Azure. It is an alternative to running the Sitecore platform on premises or via the public cloud. With Sitecore Managed Cloud, Sitecore actively monitors, manages, and maintains both the infrastructure and the applications.


So now you know everything about Managed Cloud. You might want to learn a bit about Kubernetes and using kubectl as well, but that is not the focus here. I would like to share some more experiences (and issues) we recently had in our project with SXA, JSS and NextJS that we are hosting on this Managed Cloud. Combined with the documentation and the article from David that should get you going.


Installing JSS - SXA - Horizon

In our project we are using SXA (with SPE), JSS and Horizon. In Managed Cloud these are not installed for you, but it is well documented on how to do this. You will need to make changes to json and yaml files in the Application repository - one tip: get enough sleep so you are focused.. a typo is easily made (and not always easy to find.. ). Also note space matters, and tab is no space.

Although following some documentation seems very simple, we did run into some issues. There is a mechanism that will make sure the init tasks don't override stuff that was already initialized and this blocked the installation of SXA because the Solr indexes could not be created. We contacted Sitecore Support and got a fix for the issue. A new start script was provided for our solr-init container. We added it to the solution and with a small adjustment to the DockerFile we added it into a new image that could be used to execute the installation.
ARG PARENT_IMAGE
ARG SXA_IMAGE

FROM ${SXA_IMAGE} as sxa
FROM ${PARENT_IMAGE}

COPY --from=sxa C:\module\solr\cores-sxa.json C:\data\cores-sxa.json

COPY Start.ps1 c:\

Now we were all set and ready to go. The rendering host was added and the application seemed to work. Except...

JSS sc_apikey

We have some components that need to request data from the Sitecore GraphQL endpoint at the client (e.g. to load "more" data in a list). This didn't work - the same queries did work in the rendering. Luckily the error was pretty clear and said that the sc_apikey was not provided. Which was weird because we could see the key in the header of the request. It also worked perfectly on our local containers so we assumed the issue was related to the infrastructure. 

As a workaround Sitecore Support suggested to include the sc_apikey in the querystring instead. 

We were already using a custom graphql client factory to build the url so that was a rather easy task. We did build this factory also because we already encountered issues with the needed environment variables and their availability on clients/servers, differences between local setups and development environments in the cloud and mainly because the url for the GraphQL endpoint on the rendering host is the Content Delivery but on the client it has to be your rendering host (forwarding it to the CD). Just a tip that such a factory might be a good idea if you also need to use the sc_apikey on the client.

The querystring solution works. And the header issue was related to nginx blocking headers with underscores. There is a fix available through Support though - so you can go back to using the header instead of the querystring.


JSS editing secret

When you deploy to the Managed Cloud you should use the KeyVault that is provided for you to store all kind of secrets and environment variables. Lots of them are already in there - provided by Sitecore. But you will need to add some yourself if you are using additional modules like JSS. One of those secrets is the JSS editing secret. This has to be added to the Content Management (CM) and the rendering host. A good developer is by default lazy and tends to start copy/pasting...  but in this case that was not such a good idea as apparently in the CM Sitecore called the parameter SITECORE_JSS_EDITING_SECRET and in the rendering host it has to be called JSS_EDITING_SECRET.  Just a small difference but it does break the editors and it can take some time to find out why. But now you are warned and shouldn't make that same mistake :)


Kibana

This might sound like a sunny Brazilian beach but it's actually the place to find your logs - based on Elastic Search. You'll find the information in the Sitecore docs, your url and login information in the secrets and you're good to go. 

If you are not used to this environment it can be a bit tricky to find what you are looking for. First of all: find the navigation - and go to "Discover" in the "Analytics section.  
You should get a lot of entries that might make no sense so you need to start filtering. The servicename is a good place to start: those will be like "sitecore-xm1-cm" and so on. The pod_name can also be useful if you know it. Best way for me was start simple and check the results to see which fields are available and can be used to filter further to get the logs needed.



Conclusion

Don't be afraid of the Managed Cloud. I hope I saved you some time with this blog. Read Davids article as well. Call Support when needed. And enjoy the result when it's all working.


Monday, February 7, 2022

My first JSS story

 My First JSS story: the good, the bad and the undocumented


I am what you might call a typical "Sitecore developer" - started working with Sitecore when we used to create webforms, then moved to MVC based sites, then we added some SXA and PAAS.. things changed. And as you probably know, things are changing constantly and lately in a rapid pace. A few weeks ago I also started my first headless adventure. To make it a bit more exciting the project would use the -at that moment- brand new Sitecore 10.2 with JSS and Next.JS hosted with Sitecore Managed Cloud and using containers on Docker for local development. We are also integrating with a non-Sitecore DAM and Commerce solution (both headless and SAAS) but that is .. well, out-of-scope here.
Now, as I actually don't know any of these technologies except for Sitecore that would be exciting indeed. It's been ages since I wrote some Javascript, but that has become Typescrypt and React and Next and so on now. Docker is a black box and the rest is even more unknown. If this is all very familiar to you, you can stop reading. But I do assume that is not the case for everyone.

Anyway, as I'm probably not the only adventurer our there I'd though I would share my findings and maybe it can help someone in a similar situation. This is the first post - more will probably follow. I'd like to focus on the setup in this one as that is the first step in a project.

Setting up Sitecore 10.2 with JSS and Next.JS in containers

I'm afraid I can't give you an easy setup guide or simple steps to follow for what I did as it was a bit trial and error but in the end that might have been the best way to learn a bit about what I was actually doing. What I can do is give you some tips, links to the documentation pages you'll need and positive vibes to help you do the setup as well.

But first: our requirement. We want a Sitecore 10.2 with JSS and Next.js on a local setup in containers, and it should include Horizon and SXA (we want multisite options later, so people who should know advised us to add SXA - and as that is something I know I don't mind at all).

Sitecore Containers template for Next.js

Luckily -for a lot of us- Sitecore provides a template to start with. You can find the whole walkthrough in the Sitecore docs and after a few steps you can have an up and running Sitecore environment with a rendering host. The prerequisites are pretty straight forward and most developers will already have them installed. When performing the steps in the walkthrough:
  • Make sure your Docker is setup correctly and running on Windows containers - it is mentioned but I noticed people still tend to forget
  • Stop all processes you don't need - IIS and Solr are mentioned, you might also have some local Sitecore jobs running...
  • When creating the solution in step 4, think about the parameters
    • fetchWith: I have used GraphQL here which I think is the best option at the moment as Sitecore pushes this as your best plan to be future proof - we'll get back to this later
    • prerender: I skipped this one to get the default SSG (static site generation)
Note that you get a script called "up" to start your environment. You can adapt this script - or create a whole new one - if needed. You could for example add "iisreset -stop". This is the script to get you started every morning and I noticed it is a good practice to end the day with a "docker-compose down" which is the command to bring down all your containers again gracefully.

Docker 

Docker.. containers..  this is the point where you should at least check some cheat sheet(s). Sitecore provides one for you and you can find lots more on the internet. If you get stuck or need to do something with a container, google is your friend and StackExchange usually the answer. But it is very useful to understand at least a bit about images, containers and some commands that are quite common.


Adding modules: SXA - Horizon

Next step: adding SXA & Horizon. Two modules from Sitecore that we want installed. 

While installing modules in a containerized setup you will learn to handle docker build and compose files. This is good, as you might want/need to customize more for your specific solution. To install modules you can find documentation on a few locations, and unfortunately not all are correct. Also make sure you are looking at documentation for version 10.2 - some things changed since that version so any old docs are not always valid anymore.

If you are reading the documentation from Sitecore on containers you are likely to first find the page on adding Sitecore modules. It uses SXA as an example, but this page is wrong. The general information in still valid but the examples are not.  Luckily there is also another page in the Sitecore Docker references that has does include the correct code. From this page you'll need to add everything for:
  • Sitecore Powershell Extensions (this is needed for SXA)
  • SXA
  • Horizon
And yes, SXA does not need changes to the database anymore - that is correct.

After making these changes we run our start script again which will build the containers and spin them up.

Ok, so far so good. We have installed the modules but the site that was created earlier is of course not an SXA site. 

Importing the app

First of all we are going to create a headless tenant and site. This is straight forward and very easy - especially if you have some SXA knowledge. But then we have to move our JSS app into SXA and that is more on gut feeling actually. There is documentation on moving a JSS app into SXA but that was completely not relevant. So I actually started moving items into the SXA site and changing templates to from the JSS ones to the headless-SXA ones.

I did find some documentation on 10.1 that was a bit relevant on using getting started templates with SXA. I did not follow the steps exactly - in fact, I just picked the steps that made any sense:
  • You do need to remove the site (and app) configuration in your code, but I did that in the end
  • The part about creation your solution makes you change quite some items in Sitecore, and not all of them are actually present. Don't worry - just do the steps that can be done. The documentation is for version 10.1, and I didn't find this for 10.2 yet. Also, if you configure the settings for your app (like the "Server side rendering engine endpoint URL") make sure to use the values that are defined in the config that was created for you in your solution - do not take the values from the article.
This was a bit a trial and error step but in the end the site worked (again) so we managed. 

Issues

We bumped into quite some issues still to get our first pages working.. to a point where I as really getting ****** with all this headless **** that was not making my life easier. A few tips on Slack (and some alcohol) helped us through it. Some things I remember that might help you:

CSP header issue with Horizon

Horizon didn't work - it got blocked by CSP headers. That seemed to be a known issue described on "Content-Security-Policy frame-ancestors issue after installing Sitecore Horizon". And it was easy to fix - using web.config transformations.

GraphQL issues (with SXA)

We are using the GraphQL endpoint - which is recommended in this type of setup - but that didn't seem to work with integrated GraphQL. And you do want to use that as it is a very nice way to get data into your component. The solution was found in this kb: Nested placeholders are not rendered in Layout Service. Thank you Nick W. for you time and the solution this one.

Ok - so we removed that config file and it worked. And just for those amongst you that are container-newbies (like myself): you can delete such a file by adding this command in the Dockerfile (for cm and cd):
RUN Remove-Item -Path C:\inetpub\wwwroot\App_Config\Modules\SXA\Z.LayoutService\Sitecore.LayoutService.config -Force;


Image urls

To get image urls working, you need to set the setting IncludeServerUrlInMediaUrls to false. You need to do this on the right endpoint however. Even though we are using SXA, this is not the sxa-jss one, neither the jss one, but just the default one. Or you can patch all configs... 



Tips and tricks

  1. When working with GraphQL - don't just rely on the (many) blogs out there, as the schema for the GraphQL endpoint is different compared to what is used on those blogs based on older versions. This means that some things will not be present and you need to find other solutions (e.g. images and their url including parameters and a hash). This also means however some new things are available - like sorting and paging in search queries - out of the box.
  2. If your rendering contains (new) placeholders, you need to define these placeholders in the Layout Service Placeholders field in your rendering. To do this, you need to add a PlaceholderSettings item first. The field is a treelist that lets you select such placeholder setting item(s). However, it points towards the global placeholder settings folder in the layout section of Sitecore - but in SXA we define placeholders in the sites presentation area. A possible solution: get the ID of your placeholder setting item and paste it in the field in "raw values" mode.  
  3. Use partial and page designs - very simple trick but saved us a lot of time again.
  4.  If you need to access your Solr to test queries, you can find the ip address of the container with "docker container inspect <containername>". 

I'm quite sure there is more... so more posts will be coming in the next days, weeks...  right now we are still struggling with the Managed Cloud though 😒   

My apologies for all the text which is probably very chaotic.. just like my brain after a few weeks jumping into this containered JSS bath 😵 But if I saved anyone some time in their Sitecore endeavor it was worth it. 

Hoping to read more adventures on Sitecore JSS 10.2 with NextJS soon and see you all in Budapest.