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.
- Open the
Magic.js
controller we created yesterday in your IDE. - 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 results
to 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:
- cd to the
templates/default
folder of our cartridge and create a new file calledmagicSearch.isml
. - 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 😎