Debugging Server-Side JavaScript


This article assumes some knowledge of Server-Side JavaScript. If you are new to SSJS feel free to dive into Greg Giffords articles that serve as an introduction to the topic.

SFMC Server-Side JavaScript 1: Intro
SFMC Server-Side JavaScript 1.5


If you've ever found yourself multiple days into writing Server-Side JavaScript (SSJS), with a few hundred lines of code, and a script that should work... But doesn't... Just know that we've all been there.

I tend to live in the area between total coding elation and utter frustration. In fact... I had arrived at Camp Frustration (that's right, it's an actual place), set up a tent, and camped out for the better part of two weeks. And while it was frustrating, it was also extremely rewarding and I learned so much in the process!

The script

I had written a script that aimed at taking a very long and manual process and automating it. Exporting tracking extracts and moving them to the SFTP server for a bulk period of time (one full year at a time).

This script, at every step, interacts with the SFMC APIs to create the activity definitions needed, execute them, then delete them (because they no longer 'spark joy' and are no longer needed).

During the process there were moments where I felt like an API super hero, but there were also moments that made me want to toss my computer out of my office window and give up (don't worry, I didn't). The most glaring issue was that no matter how many iterations my loop ran, it would only create the first tracking extract; but there were no errors and everything else ran perfect!

I quickly learned the value of being able to debug and troubleshoot things (not just API calls) in SFMC.

Setting up your environment

Testing is typically done on a cloud page or a code resource page. I've done most of my testing from the classic > code view cloud page editor.

My first tip is something I picked up recently from a fellow EmailGeek as I was getting frustrated that the cloud page was running my SSJS code as it rendered the preview:

%%[
if indexof(requestparameter('pageurl'),"CloudPages/Preview") < 1 then
]%%
                                                                
//Your Code                                                                
%%[
endif
]%%

This script captures the page URL and checks if the context of it is a preview. Using some standard AMPscript logic, it tells the page to only load the code you are running on a non-preview page.

Callbacks

When you're working with the SFMC APIs, you not only need to instruct it what to do, you you also need to tell it to provide you with the details of what it just did. But why do you need to know what it just did? We trust SFMC, right? Well... Kinda... But this serves a few purposes. These Callbacks tell us what the values of the variables we are passing into the API calls are before they are used and what the response from the API call is.

So how do we get the callback and where should we put it?

With SSJS we don't have the console to display our debugging efforts since everything is run server-side, so we use the Write function in it's place.

In order to make your callback accessible, your API call needs to be set as a function expression; which means calling your function as variable. This stores the response in that variable so you can display or parse through it.

var createExtract = ...

Once your response is captured by the variable, you can use the Stringify function along with Write to display your response.

Something I've learned in this process is to leave yourself context with the details you are displaying.

Write(Stringify(createExtract)); will provide you with the callback but Write('Create extract definition: ' + Stringify(createExtract) + '<br><br>'); will give you what the response of for and some extra space so it doesn't run into your next callback.

Positioning of your callbacks

Where the callback details are generated from was something that I didn't think of until very late in the game; and it would have saved me a lot of time.

When writing SSJS, I like to use functions so my code is reusable. At the onset, I was putting my callbacks directly after I initiated my function

var createExtract = createExtract (...);
Write(Stringify(createExtract));

While this was giving me some insights into what was going on... It wasn't the best place for it. I was not getting the details closest to where the actual call was being made.

Verify your payloads

What's a payload? The payload is simply the data you pass through your API call. It contains all of your information to create/interact with the API object.

This was my Ah Ha! Moment.

I was checking everything after the HTTP.Post call was being made, and I thought that because I was displaying my variables before I initiated my createExtract function I was verifying the correct data was being passed (I was... And I wasn't).

To truly verify what was being passed into my function I went back to the trusty Stringify/write combination and displayed it before my HTTP.Post in the function.

What did this tell me? By adding this response here, this showed me that after my first loop iteration, the createExtract function was either not initiating or not injecting my variables into the payload (which because it's a function, should be the same as the first iteration).

Try/Catch

The try/catch function is not something native to SSJS but it's one of the functionality in Vanilla JavaScript that works with it.

As with a lot of other instances, the error messages provided by SFMC tend to leave a lot to be desired. With the try/catch block, you are able to display a more detailed error message for that specific part of your code.

Try {

//Your code

} catch(e) {
   Write(Stringify(e));
};

My initial thought was that it's not running the function after the first iteration, but it's holding out the error. So I wrapped my function and settled in to find out what my error was.

Try {

var createExtract = createExtract (...);

} catch(e) {
   Write(Stringify(e));
};

Much to my surprise, the try/catch had a very welcomed but unexpected effect... It fixed the issue! The current theory behind why this worked is that try/catch is local scope only (visit here for more information on scope), so nothing can be cached for the second run. In the event that we are able to get more insight into this issue and why the try/catch worked, I will update this article.


The next time you find yourself waist high in code with no idea why it doesn't work and you're ready to throw in the towel (and the laptop too!) remember there are some very useful methods you can use to help troubleshoot and debug your code.

Keep in mind, this is not an exhaustive list of debugging techniques. There are other methods and best practices to keep in mind as you work through writing and troubleshooting your code.

All Rights Reserved
Made with by your fellow SFMC users.
All Rights Reserved
Made with by your fellow SFMC users.