Gravid Banner

Updating AWS S3 get object Lambda function to Node 22

One of the annoying things about software engineering is that working code doesn’t necessarily stay working. About eight years ago I wrote an Amazon Web Services Lambda Function using Node JS (I think version 10). Five years ago when the node version was end of life I updated it to Node 12 with no changes needed and it has been working ever since. I’ve had a few warnings from AWS about code relying on versions of node that are end-of-life but it was still working when I tested it.

exports.handler = (event, context, callback) => {
  var s3Bucket = "my-bucket-name";
  var key = "my-item-key";

  var AWS = require('aws-sdk');
  var s3 = new AWS.S3({region: 'eu-west-1'});

  var getFromAWS = function() {
    var params = {Bucket: s3Bucket, Key: key1};

    s3.getObject(params, function(err, data) {
      if(err) {
        itemJson = "[\"Error\":[\"Key not found\"]]";
        console.log(err, err.stack); // an error has happened on AWS
      }
      else {
        itemJson = data.Body.toString('UTF8');
      }
    }
    if (itemJson != "") {
      callback (null, {"key":key1, "data":itemJson});
    });
  };

  getFromAWS();
};
JavaScript

This week I needed to make a minor change to it, but in order to so I needed to update it to a later version of Node. As soon as I did so I got the following error.

Error: Cannot find Module ‘aws-sdk’

A quick google confirmed the issue, aws-sdk is no longer bundled as standard with Node on AWS lambda. This is slightly annoying as there are plenty of even recent articles which use the var AWS = require(‘aws-sdk’) syntax.

After reading up on it I decided it was probably time to bring the module fully up to date. In order to do that I would need to use AWS SDK version 3. Firstly I needed to import the required modules, and secondly I needed to use a different syntax to do the s3 Get Object command

import {S3Client, GetObjectCommand} from '@aws-sdk/client-s3';

var s3 = new S3Client({region: 'eu-west-1'});

const response = s3.send(new GetObjectCommand({
      Bucket: s3Bucket,
      Key: key,
    }));
JavaScript

Unfortunately as soon as I tried to make those changes I got a second error message.

Uncaught SyntaxError: Cannot use import statement outside a module

A quick search revealed that the simple solution to this is to ensure that the code was named index.mjs instead of index.js to show node that it was a module.

With these fixes in place I was able to rewrite the whole function.

Asynchronously Get Objects from S3 in parallel

I needed to be able to return two json files from S3. In order to keep the Lambda execution as short as possible I wanted to run the two commands in parallel. To achieve this I call the async getFromAWS function without using await, and then await both results in the highlighted line.

The full code of the working version is included below.

import {S3Client, GetObjectCommand} from '@aws-sdk/client-s3';

export const handler = async (event) => {
  var s3Bucket = "my-bucket-name"";
  var s3 = new S3Client({region: 'eu-west-1'});

  const getFromAWS = async(key) => { 
    const r = await s3.send(new GetObjectCommand({
      Bucket: s3Bucket,
      Key: key,
    }));
    
    const json = await r.Body.transformToString();
    return json;
  };

  var response;

  try {
    const firstJson = getFromAWS("my-item-key1");
    const secondJson = getFromAWS("my-item-key2");

    const result = [await firstJson, await secondJson];

    response = {
      "data1":JSON.parse(result[0]),
      "data2":JSON.parse(result[1])};
  } catch (e) {
    console.log(e);
    response = {"Error":"An Error Occurred"}
  }

  return response;
};
JavaScript


Comments

Leave a Reply

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