0

AWS – Writing better code in Node.js for Lambda functions

Hey,

Been a while but finally its time to post some of technical information I accumulated since my last post. Today we will focus on improvements in code from the time you released something aka version 1 into a code base which you can run tests against.

 

The past …

In my last post I described some modular code I started to work on for use with Lambda ( available here ) The code back then contained a lot of redundant blocks ( which I was aware of 😉  ) however it enabled me to focus on making it better. Just to get everyone idea how it did look like here is snippet from initial code commit

....  // code removed for demo purposes // .... 

var timestamp = new Date().getTime();
  const uniqueId  = uuid.v1();

  console.log(`[CreatePlatformEndpoint] [${timestamp}] [${uniqueId}][Info] Starting execution`);
  
      var responseCode = 400;
      var responseBody = "";
  
      var response = {
        statusCode: responseCode,
        body:       responseBody
      };

      if ( !isDef(event.body) )
        { 

          console.log(`[CreatePlatformEndpoint] [${timestamp}] [${uniqueId}][Error] Missing body information (EC.001)`);

          let errorData = {
            code: "EC.001",
            data: {
              message: "Missing body"
            }
          }

          response.body = {
            action:  "CreatePlatformEndpoint",
            status:  "error",
            error:   errorData,
          }

          response.body = JSON.stringify(response.body)

          callback(null,response); 

        }

      var jsonBody = JSON.parse(event.body);

      console.log(`[CreatePlatformEndpoint] [${timestamp}] [${uniqueId}][Info] Parsed body from request`);

      if ( !isDef(jsonBody.deviceToken) || !isDef(jsonBody.platformApplicationArn))
        {

          console.log(`[CreatePlatformEndpoint] [${timestamp}] [${uniqueId}][Error] Missing required parameters in body (EC.002)`);
          
          let errorData = {
            code: "EC.002",
            data: {
              message: "Missing required parameters in body"
            }
          }

          response.body = {
            action:  "CreatePlatformEndpoint",
            status:  "error",
            error:   errorData,
          }

          response.body = JSON.stringify(response.body)

          callback(null,response);

And now this is just only portion of  code which been redundant. It was just in single file. Now imagine we have multiple components with multiple files and we need to make a single change into logic which is repeated across all those ?! Madness :/

Therefore it took some time ( as I’m far away from being a js developer 😉 ) but I changed …

Making it better …

by creating classes and also in this way trying to regain control on controlling the lifetime of object’ instances. This also enabled me to start writing tests for my code which I’m really happy about as it help so much in test driven development ( this was inspired by the following article )

  • Write your business logic so that it is separate from your FaaS provider (e.g., AWS Lambda), to keep it provider-independent, reusable and more easily testable.

  • When your business logic is written separately from the FaaS provider, you can write traditional Unit Tests to ensure it is working properly.

  • Write Integration Tests to verify integrations with other services are working correctly.

 

So how does the new code looks like ? Take a sneak peak on  snippet from shared resource

const uuid = require('uuid');

class xSharedFunctions { 
    
    constructor(component,callback,disableLogging){
        
                this.callback       =  callback;
                this.component      =  (component === null || component === undefined ) ? 'undefined' : component ;
                this.disableLogging =  disableLogging
                
            }

    generateSuccessResponse(dataSuc,respCode){
            let that = this;

            var responseCode = (respCode === null || respCode === undefined  ) ? 200:respCode ;
            var responseBody = "";
        
            var response = {
              statusCode: responseCode,
              headers: {
                "Access-Control-Allow-Origin" : "*",      // Required for CORS support to work
                "Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
              },
              body:       responseBody
            };
            
            response.body = {
                component: that.component,
                status:  "success",
                data: dataSuc
            };
            
            response.body = JSON.stringify(response.body);
        
            return response;
    }

// REST OF CODE COMES HERE ....

 

Once that is in place we can go ahead and try to …

 

… use the code in our modules/applications

To get all required references we required our resources and use their functions

'use strict';

var xSharedFunctions = require('../xRes/shared/xSharedFunctions');
var xSnsEndpointManager = require('../xRes/xSnsEndpointManager');


const uuid      = require('uuid');
const component  = 'sns'

var xSharedFnc = new xSharedFunctions('sns');

module.exports.create = (event, context, callback) => {
  const uniqueId      = uuid.v1();
  var xSnsEndpointMgr = new xSnsEndpointManager(uniqueId,callback);

  xSharedFnc.logmsg(uniqueId,'info','Starting execution');
  xSharedFnc.logmsg(uniqueId,'info',`${JSON.stringify(event)}`);

// REST OF CODE COMES HERE ...

 

with keeping the above in mind we should not forget to …

 

… test our code 🙂

And that is why for example I got rests which looks like the following now ( using Mocha and Babel ) …

// CODE REMOVED FOR VISIBILITY ... 


describe('xSnsEndpointManager', function() {
    
        describe('#createPlatformEndpoint()', function() {
    
                before(function () {
                    


                    AWS.mock('SNS', 'createPlatformEndpoint', function (params, callback) {
                    callback(null, '{"ResponseMetadata":{"RequestId":"efdb1199-f10e-5b0b-bff9-43addbda438b"},"EndpointArn":"arn:aws:sns:eu-west-1:12345:endpoint/APNS_SANDBOX/blah-app/c08d3ccd-3e07-328c-a77d-20b2a790122f"}')
                    })

                })


                it('should create endpoint if token provided', function(){

                    var xSnsEndpointMgr = new xSnsEndpointManager('1234',function(dummy,responseCallback){
                        expect(responseCallback.statusCode).to.equal(201);

                        let result = JSON.parse(responseCallback.body);
                        expect(result.component).to.equal('sns');
                        expect(result.status).to.equal('success');

                        let resultData = JSON.parse(result.data);
                        expect(resultData.EndpointArn).not.to.equal(null);
                       
                    },true);

                    let res = xSnsEndpointMgr.createPlatformEndpoint('eee','eee');
                    
                });

                after(function () {
                    AWS.restore('SNS', 'createPlatformEndpoint')
                  })

          });
    
});

// CODE REMOVED FOR VISIBILITY ...

 

Closing thoughts …

So as you can see it all starts to look nice and definitely will get you further if you implement tests. For those interested to see how do I do things here are the links to my repositores on git

 

I hope someone would be able to reuse something for their own needs 😉 Happy coding!

 

rafpe

Leave a Reply

Your email address will not be published. Required fields are marked *