Have you ever wondered how Salesforce Commerce Cloud, especially SFRA (Storefront Reference Architecture), handles the rendering of pages based on controllers and routes?

It’s like embarking from point A to point B, with controlled detours and sudden stops. This blog will explore how SFRA allows us to navigate these situations and the various options available at different locations.

Let’s dive in!

Global Hooks

Before we discuss the SFRA specifics, let’s start with some global options that allow us to execute code for any request (SFRA or not).

For the technical details, please read this blog post by Johnny Tordgeman.

onRequest

The onRequest hook in SFCC allows you to intercept and modify an incoming HTTP request before the system processes it.

This hook is commonly used for session validation, request validation, or custom logging tasks.

Note

Cached URLs

This hook is executed for all requests, even if the Web Adapter (page cache) caches them.

Warning

Dangerous

Whilst this hook provides a lot of flexibility, it also hooks into every request. So, any exception or delay introduced by your custom code will be reflected on all pages and requests.

onSession

The onSession hook is a server-side hook executed at the beginning of each new session. This hook allows you to perform custom logic or set session attributes before the session is initialised.

It can be used to customise the session behaviour (like plugin_slas), such as setting default values, checking for certain conditions, or performing any necessary setup before the session is fully established.

Warning

Dangerous

Like the onRequest hook, any delay or exception introduced here can be devastating.

SFRA Routes

Standard Home.js controller with Show and ErrorNotFound routes.
The 'home.js' controller file of SFRA

Before we get started, we need to ensure we are on the same page on what a “route” is.

In the context of SFRA (Storefront Reference Architecture) of SFCC (Salesforce Commerce Cloud), a controller route is a mapping between a URL and a specific controller function. When a user navigates to a specific URL within the SFRA storefront, the controller function, a key element of the SFRA architecture, handles the request and generates the appropriate response (usually ISML or JSON).

The standard available options, and the most common ones, are:

  • GET
  • POST

These will serve as the ‘base route’, the starting point of our project. But remember, this is just the beginning. We have the power to extend and customize this base route of SFRA, as we’ll discover in the options outlined in this blog post.

SFRA Server functions to extend and replace

Note

Cartridge Path

In this example, we are assuming that there is only one extra cartridge in the cartridge path. This simplifies the explanation, as adding more than one cartridge to the path with an expanding function would make it more difficult to understand.

Flow diagram of the standard Home-Show controller logic.
The standard Home-Show controller logic visualised
Cartridge path: plugin_custom:app_storefront_base

server.prepend()

The server.prepend function adds a middleware function to the beginning of the route stack. This allows you to execute code before the base (app_storefront_base) processing begins.

Here’s a simple example of how you can use server.prepend with the homepage function:

server.prepend('Show', function (req, res, next) {
 // Your code here will be executed before the app_storefront_base
 next();
});
SFRA prepending of Home-Show
Visualising what 'prepending' does in a single route (Home-Show)

Note

Fun Fact

Prepending was one of the first pull requests I had made to SFRA.

server.append()

The server.append function adds a middleware function to the end of the route stack. This allows you to execute code after the base (app_storefront_base) processing finishes.

Here’s a simple example of how you can use server.append with the homepage function:

server.append('Show', function (req, res, next) {
 // Your code here will be executed after the show function in app_storefront_base
 next();
});
SFRA appending of Home-Show
Visualising what 'appending' does in a single route (Home-Show)

server.replace()

The server.replace function replaces the entire route stack up until that point. This allows you to replace code in the base (app_storefront_base) fully.

Here’s a simple example of how you can use server.replace with the homepage function:

server.replace('Show', function (req, res, next) {
    var Site = require('dw/system/Site');
    var PageMgr = require('dw/experience/PageMgr');
    var pageMetaHelper = require('*/cartridge/scripts/helpers/pageMetaHelper');
    pageMetaHelper.setPageMetaTags(req.pageMetaData, Site.current);
    var page = PageMgr.getPage('homepage');
    if (page && page.isVisible()) {
        res.page('homepage');
    } else {
        res.render('home/homePage');
    }
    next();
});
SFRA replacing of Home-Show
Visualising what 'replacing' does in a single route (Home-Show)

SFRA Route Hooks

The options explained above already give you quite a bit of flexibility. But what if I told you there is even more to come? The route itself also exposes a few “events” in which we can hook into:

  • route: Start: Executes at the start of the route, before any middleware defined using server.*
  • route: BeforeComplete: Executes after all route middleware is finished but before the “route:Complete” event.
  • route: Complete: The final event, after everything else.
  • route: Step: Executed between each route middleware.
  • route: Redirect: Executed when a “res.redirect()” is executed.
server.replace('Show', function (req, res, next) {
     this.on('route:BeforeComplete', function (req, res) {
        var viewData = res.getViewData();
       // Your custom logic, executed at the end of the route
    });
    next();
});

Bringing it all together

Combined diagram of SFRA route middleware and hook extension points.
Bringing all of the options together!

Multiple Cartridges

The cartridge path influences the order in which SFRA middlewares are executed.

Cartridges higher up in the path are given precedence over those lower down. This means that the middleware in cartridges at the beginning of the path will be executed before those in cartridges further down the path.

New to the concept of “cartridge path”? Have a look at this trail!

Tip

Try to experiment

When working across multiple cartridges, make use of your debugger (e.g. Prophet) to check in what order your code is executed on the server side to understand the order of execution fully.

Warning

Don’t go overboard

Remember that the complexity of adjusting a route is not something to brush over, especially if people often change on the project. Keep your code well structured and the order of execution of every middleware documented.