Using Server-Side JavaScript in Optimizely Event Handlers

In December 2021, I threw together a proof of concept for delegating event handler logic in Optimizely Content Cloud to sandboxed JavaScript, executed server-side.

For this particular POC, I was able to change the URL setting logic from the Edit Mode UI and have that logic used without redeploying any file-based code. Traditionally, this is something that has required custom C#, deployed into the runtime.

I used Jint which is an open-source C# wrapper around ECMAScript.

I stored the JS on a start page property called UrlSegmentBuilder. This could be edited from the UI (a better IDE would have been helpful).

This particular code set the URL segment of a new page to the current date (ex: 2022-01-05).

The way I wrote it, you just needed to set the data.Segment value in the JS. (But, see below, because there are a bunch of other ways to get the data in and out of Jint. I don’t know what the best way would be – this would require some testing and feedback.)

Here is the event handler code I used. Clearly, this would need to be generalized, include error handling, and use some execution constraints for safety.

urlSegmentCreator.Creating += (s,e) =>
{
  // Get the raw JS code
  var startPage = repo.Get<PageData>(ContentReference.StartPage) as StartPage;

  if(!string.IsNullOrWhiteSpace(startPage.UrlSegmentBuilder))
  {
    // This is the object that will "carry" the data into and out of the JS
    // (See note below. I should have been able to do this with a string.)
    var data = new Data(){ Segment = e.RoutingSegment.RouteSegment };

    var engine = new Engine()
      .SetValue("data", data) // Push the data in...
      .Execute(startPage.UrlSegmentBuilder); // ...execute the JS...

    e.RoutingSegment.RouteSegment = data.Segment; // ...read the data out
  }
};

(I should have been able to use a string, but for some reason I couldn’t get that value to set and be readable outside the JS code. I had to create a simple object – the Data class – and set the property. Not sure why.)

There are other ways to get data in and out:

  • Call an output JS function, that maps to a C# method
  • Use the Evaluate method to directly return an assignable C# value
  • Call GetValue after execution, to read a JS variable into C#

The first time my POC was executed on start-up, it look 64ms, but I think most of this was for parsing, or maybe just some startup lag. On subsequent executions, it didn’t even register – it reported 0ms execution.

I measured it in ticks, and it was something like 64,000, which is about two-thirds of one millisecond.