Customize SFRA Controllers and Routes

You can customize SFRA controllers and routes to work for your own storefront.

Inheriting Functionality from Another Controller and Extending It

It's important to understand when to extend a controller and when to override it, because this decision can significantly impact functionality and performance.

When do I want to Override?

It's best to override if you want to avoid executing the middleware of the controller or script you're modifying.

When extending a controller, you first execute the original middleware, and then execute the additional steps included in your extension. If the original middleware steps include interaction with a third-party system, that interaction is still executed. If your extension also includes the interaction, the interaction is executed twice. Similarly, if the original middleware includes one or more steps that are computationally expensive, you can avoid executing the original middleware.

When do I want to Extend?

If the middleware you want to override is looking up a string or performing inexpensive operations, you can extend the controller or module.

How do I extend or Override?

Use the module.superModule mechanism to import the functionality from a controller and then override or add to it.

Example: Adding Product Reviews to Product.Js

This example customizes the product detail page to include product review information. The code for this example is available in the Plugin_reviews demo cartridge.

In this example, the Product.js controller uses the following APIs for customization:
  • module.superModule: Imports functionality from the first controller with the same name and location found to the right of the current cartridge on the cartridge path.
  • server.extend: Inherits the existing server object and extends it with a list of new routes from the super module. In this case, it adds the routes from the module.superModule Project.js file.
  • server.append: Modifies the Show route by appending middleware that adds properties to the viewData object for rendering. Using server.append causes a route to execute both the original middleware chain and any additional steps. If you're interacting with a web service or third-party system, don’t use server.append.
  • res.getViewData: Gets the current viewData object from the response object.
  • res.setViewData: Updates the viewData object used for rendering the template. Create your customized template in the same location and with the same name as the template rendered by the superModule controller. For example, if this controller inherits functionality from app_storefront_base, the rendering template depends on the product type being rendered. The rendering template can be either product/productDetails, product/bundleDetails, or product/setDetails.
// Product.js

'use strict';

var server = require('server');
var page = module.superModule;        //inherits functionality from next Product.js found to the right on the cartridge path
server.extend(page);                  //extends existing server object with a list of new routes from the Product.js found by module.superModule

server.append('Show', function (req, res, next) { //adds additional middleware
    var viewData = res.getViewData();
    viewData.product.reviews = [{
        text: 'Lorem ipsum dolor sit amet, cibo utroque ne vis, has no sumo graece.' +
          ' Dicta persius his id. Ea maluisset scripserit contentiones quo, est ne movet dicam.' +
          ' Equidem scriptorem vis no. Civibus tacimates interpretaris has et,' +
          ' ei offendit ocurreret vis, eos purto pertinax eleifend ea.',
        rating: 3.5
    }, {
        text: 'Very short review',
        rating: 5
    }, {
        text: 'Lorem ipsum dolor sit amet, cibo utroque ne vis, has no sumo graece.',
        rating: 1.5
    }];

    res.setViewData(viewData);
    next();
});

module.exports = server.exports();

Replacing or Adding a Route

If you want to completely replace a route, rather than append it, use module.superModule to inherit the functionality of the controller and route you want to replace. Then register the functions you want the route to use.

Example: replacing the Product-Varation route

In your custom cartridge, create a Product.js file in the same location as the Product.js file in the base cartridge. Use the following code to import the functionality of Product.js and redefine it.

var page = require('app_storefront_base/cartridge/controller/Product');
var server = require('server);

server.extend(page);

server.replace('Show', server.middleware.get, function(req, res, next){
    res.render('myNewTemplate');
    next();
});

Overriding Instead of Replacing a Step in the Middleware Chain

We recommend that you replace a route to change individual steps in a middleware chain.

You can't change a step in the middleware chain. However, you can either replace the whole route or append a new step after the chain completes, but before the template renders. Usually, if you're appending a step, it's to override or add data to the ViewData object.

Important: Be careful when appending existing routes. If you append a step, the existing middleware chain executes no matter what the appended step does. Therefore, it’s possible to execute the route twice. If the route calls a web service or updates a third-party system, such as an inventory management system, it can take the same action twice.
Note: Information about middleware filtering classes is available in the SFRA JSDoc in the server-side documentation for Global.

Changing the Access on an Existing Route

You can use the middleware functions provided by Commerce Cloud or create your own. We recommend that you replace a route when changing access.

These middleware filtering functions are provided by Commerce Cloud:

  • get: Filter for get requests
  • htt: Filter for http requests
  • https: Filter for https requests
  • include: Filter for remote includes
  • post: Filter for post requests

If the request doesn't match the filtering condition, the function returns an Error with the text Params do not match route.

Customizing a Controller with a Web Service or a Call to a Third Party

In general, customizing a controller with a web service or third-party call uses the same mechanisms as other types of customizations. However, it's important to make sure that you don't execute the controller twice. Executing the controller twice is possible if you use server.append to customize the ViewData object passed to the rendering template. Instead, simply replace the route entirely.