October 25, 2023

Let’s say we want to query an external REST API, and will use LocalServiceRegistry for this. What is the best approach for creating our service with a proper encapsulation?

With our service implementation we want to archieve the following goals:

  • don’t throw an error in case the service isn’t created in the Business Manager yet

  • keep any errors within the service, so the success status is available on the dw.svc.Result object

  • read mock responses from an ISML template

  • have the communication log available on production if required

  • have a flexible way for calling multiple endpoints of the same service

Our actual implementation could now look like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
let LocalServiceRegistry = require('dw/svc/LocalServiceRegistry');
let myQueryService;

function getDetails(operation) {
        switch (operation) {
        case module.exports.OPERATION_QUERY:
                return {
                        method: 'GET',
                        resourceURL: '/query',
                        mockType: 'query'
                };
        default:
                throw new Error('Operation unknown: ' + operation);
        }
}

function getMyQueryService() {
        return LocalServiceRegistry.createService('my.query.service', {

                 mockFull : function(service, operation) {
                        log.info('Doing mock call for: {0}', operation);

                        let Template = require('dw/util/Template');
                        let details = getDetails(operation);
                        let template = new Template('query/mock_' + details.mockType + '.isml');
                        return template.render();
                },

                createRequest: function(service, operation, params) {
                        const details = getDetails(operation);

                        service.setRequestMethod(details.method);
                        let baseUrl = service.configuration.credential.URL;
                        let resourceUrl = operationDetails.resourceURL;
                        if (operation === module.exports.OPERATION_QUERY) {
                                resourceUrl += '?param1=' + encodeURIComponent(params.param1);
                        }
                        service.setURL(baseUrl + resourceUrl);

                        return null;
                },

                parseResponse: function(service, responseObject) {
                        if (responseObject.statusCode !== 200) {
                                throw new Error('Invalid service response: ' + responseObject.statusCode);
                        }
                        return responseObject.text;
                },

                filterLogMessage: function(msg) {
                        return msg;
                }

        });
};

module.exports.get = function get() {
        if (empty(myQueryService)) {
                myQueryService = getMyQueryService();
        }
        return myQueryService;
};

module.exports.OPERATION_QUERY = 'OPERATION_QUERY';

With this service in place, we can use it and call our query endpoint with the following snippet:

1
2
3
4
5
6
7
8
let MyQueryService = require('*/.../MyQueryService');
let myQueryService = MyQueryService.get();
let queryResultStatus = myQueryService.call(
        MyQueryService.OPERATION_QUERY,
        {
                'param1': one
        }
);

If queryResultStatus.ok is true, our call was successful. This will archieve our defined goals, keeps the overall architecture fairly simple and makes our service maintainable in the long run!

Contact