Tech & Engineering Blog

11 Days of Salesforce Storefront Reference Architecture (SFRA) — Day 7: More Services Framework

Written by Johnny Tordgeman | May 12, 2020 6:24:00 PM

Yesterday we built our first service using the Services framework, and as much joy as we got from the dad jokes it returned, there was something much less funny about the whole thing: it was static 😱. We had one URL we were calling and that is. No paths, no on-demand headers, no query params — nothing. Well, today we are going to change all that! We are going to build the ultimate dad jokes widget that allows you not only to display a random dad joke (as we did yesterday) but also search a dad joke on a specific term 🤓

Searching for the Perfect Dad Joke

icanhazdadjoke.com allows us to search for a dad joke using its search endpoint: https://icanhazdadjoke.com/search and a query param: term, for the search term. Let’s extend the Services Framework entity we created yesterday to be able to handle the new /Magic-Search route.

  1. Open the Magic.js controller we created yesterday in your IDE.
  2. Below the Show handle function, add the following function:
server.get('Search', function (req, res, next) {
    var properties = {};
    var template = 'magicSearch';
    var searchTerm = req.querystring.term || '';

    var url = service.dadJokeAPIService.getURL() + 'search';
    var svcResult = service.dadJokeAPIService.setURL(url).addParam('term', searchTerm).call();
    if (svcResult.status === 'OK') {
    	properties.term = searchTerm;
        properties.jokes = svcResult.object.results;
    }

    res.render(template, properties);
    next();
});

Let’s go over what we have here:

Line 3:Sets magicSearch as the template that the controller will render (we will create it in the next section).

Line 4: Gets the term query parameter from the req object’s querystring object. This querystring object holds all the query parameters passed to the SFRA controller, and each query parameter can be accessed by specifying its name.

Line 6: The getURL() method returns the URL set in the Services Framework entity credentials. We can use the result of this method to append any path we wish for the service, and in our case, the search path.

Line 7: This line introduces two new methods: setURL() and addParam():

* setURL() is used to set the URL for the service call, overriding the static one defined in the Services Framework entity’s credentials. In case the entity does not use any credentials ,setURL() will define the URL the service will call.

* addParams() is used to add parameters to the service call. If your service is using the GET method, the parameters will be added as query params. If your service is using the POST/PUT method , the parameters will be added as body parameters.

In our implementation we use setURL() to set the service call to the URL we created in line 6, and addParams() to add the term parameter needed by icanhazdadjoke.com’s search endpoint.

Finally, we use the service object’s call method to make the actual call to the icanhazdadjoke.com service and store the result with the svcResult variable.

Lines 8–11: If the service call was successful, the svcResult’s object property will hold the JSON response from the service. This JSON response contains a results property that holds an array of joke objects. We will save resultsto a new property on our properties object called jokes, and the search term to a new property called term.

Line 13: Calls the render method of the res object, passing it the properties object we created in line 2. This object can later be used in an ISML template when it renders.

Line 14: Calls the next() method passed as an argument to the function, which calls the next middleware in the chain.

Rendering the Laughs

With the new route handler under our belt, let’s create the ISML template that it will render:

  1. cd to the templates/default folder of our cartridge and create a new file calledmagicSearch.isml.
  2. Add the following to the new template:
<iscontent type="text/html" charset="UTF-8" compact="true"/>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Magic Search!</title>
</head>
<body>
    <div class="header">
        <h1>🤓 Dad jokes matching the term ${pdict.term}:</h1>
    </div>
    <hr/>
    <div class="content">
        <ul>
	    <isloop items="${pdict.jokes}" var="jokeObject">
	        <li>${jokeObject.joke}</li>
	    </isloop>
	</ul>
    </div>
</body>

Most of the code in this template is nothing new, so let’s focus on the interesting part — the loop of jokes:

Lines 14–17: These lines introduce the isloop element. This element enables us to iterate through an array of items, similar to other JavaScript iterators such as forEach. Here we loop through the jokes collection of the pdict object (go back and read yesterday’s post if you don’t remember what the pdict is) while naming each iteration as jokeObject (as it contains a joke object, similar to the one we saw yesterday), and add it’s joke property as a li element to the unordered list (ul element) on the page.

Go Wild 🌈

In order to test our new route and template we need to upload the cartridge back to SFCC. Using our favorite command npm run uploadCartridge on the root of our cartridge folder will take care of that.

To test our search route, use its base url (should be something like https://.demandware.net/on/demandware.store/Sites-RefArch-Site/default/Magic-Search) and append a query parameter named term with the value you want to search for. The end result looks like this: https://.demandware.net/on/demandware.store/Sites-RefArch-Site/default/Magic-Search?term= .

If all went well you should get a page similar to this:

Guess which term I searched for here?

That wraps up our Services Framework fun. Of course, there are other properties you can use with your service, such as .addHeader(name, value) to add a header to the request, or setRequestMethod(method) to change the HTTP method (such asPOST or PUT) or even use the setOutFile(file)method to set an output file to store the service response, however, we will save working with these for later.

Tomorrow we will have fun with a very important aspect of working with cartridges — Events (and specifically the onRequest event).

As always, looking forward to your comments either here or on Twitter 😎