Creating and using a lambda layer inside lambda function in NodeJS v20 — Part 2

Vanu Protap Verma
5 min readJun 22, 2024

--

Objective

In this multi-part post, I will try to provide you the steps of creating a lambda layer in AWS and then use that lambda layer inside a lambda function. This is part-2 of this series. You can read the part-1 here.

Let’s continue.

Part — 2: Goal

I want to write a lambda function, that will use custom JWT verify service from my lambda layer.

Steps

Creating a lambda function is very easy. You can follow this guidelines to create a lambda function. Please note that, since this post focuses on NodeJS v20.x, you need to select Node.js 20.x as the runtime for your lambda function. Otherwise, you may face some issues. I have created my lambda function like below:

Once the function is created, open the lambda function. You should see something like below:

As you can see, our function does not have any layer attached to it yet. Let’s do that now.

To add a layer, you can follow this guideline. Please remember to select the lambda version carefully (if you have multiple versions for a layer).

Now that our lambda function has a layer attached to it, it’s time to use some custom code coming in from our layer.

Well, this part is a bit tricky as I found the AWS documents a little hard to understand. In this document, AWS mentioned that the codes of a lambda layer codes are extracted inside /opt directory in the function execution environment. And in this document, AWS has outlined different paths for different NodeJS versions.

Unfortunately, I haven’t found any example of how it should be if we want to use a custom module as our lambda layer. Therefore, I had to do the below checks inside my lambda function.

First, I added a console.log(process.env.NODE_PATH) inside my function, like this:

export const handler = async (event) => {
console.log(process.env.NODE_PATH);
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};

// output for console.log
// /opt/nodejs/node20/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task

So, to find out actually where my layer code has been extracted, I wrote a simple directory read function, like the below code, with /opt/nodejs as the location.

 import fs from "fs";


async function readDirContent() {
return fs.readdir("/opt/nodejs", (err, files) => {
files.forEach(file => {
console.log(file);
});
});
}

export const handler = async (event) => {
console.log(process.env.NODE_PATH);

await readDirContent(); // newly added


// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};

This gives me the below result:

Test Event Name
temp

Response
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}

Function Logs
START RequestId: 49ab3ff1-7d81-4492-ac35-c69331a003b8 Version: $LATEST
2024-06-22T12:40:28.386Z 49ab3ff1-7d81-4492-ac35-c69331a003b8 INFO /opt/nodejs/node20/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task
2024-06-22T12:40:28.425Z 49ab3ff1-7d81-4492-ac35-c69331a003b8 INFO .DS_Store
2024-06-22T12:40:28.425Z 49ab3ff1-7d81-4492-ac35-c69331a003b8 INFO node_modules <-- this is what we are looking for
END RequestId: 49ab3ff1-7d81-4492-ac35-c69331a003b8
REPORT RequestId: 49ab3ff1-7d81-4492-ac35-c69331a003b8 Duration: 40.92 ms Billed Duration: 41 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 129.16 ms

Request ID
49ab3ff1-7d81-4492-ac35-c69331a003b8

From this result, please note that there isn’t any node20 folder as mentioned here even though we have defined Node.js 20.x as our runtime.

Now, I took the node_modules folder name from the result above and added it at the end of my readdir function’s starting point, so that it now becomes /opt/nodejs/node_modules and re-run the function again.

import fs from "fs";


async function readDirContent() {
return fs.readdir("/opt/nodejs/node_modules", (err, files) => {
files.forEach(file => {
console.log(file);
});
});
}

export const handler = async (event) => {
console.log(process.env.NODE_PATH);

await readDirContent();


// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};

Which gives me this result

Test Event Name
temp

Response
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}

Function Logs
START RequestId: e14cc29b-9646-4f57-92b0-9625aefa08cb Version: $LATEST
2024-06-22T12:54:39.893Z e14cc29b-9646-4f57-92b0-9625aefa08cb INFO /opt/nodejs/node20/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task
2024-06-22T12:54:39.941Z e14cc29b-9646-4f57-92b0-9625aefa08cb INFO .DS_Store
2024-06-22T12:54:39.941Z e14cc29b-9646-4f57-92b0-9625aefa08cb INFO custom_jwt_verify_service <-- our custom module
END RequestId: e14cc29b-9646-4f57-92b0-9625aefa08cb
REPORT RequestId: e14cc29b-9646-4f57-92b0-9625aefa08cb Duration: 49.80 ms Billed Duration: 50 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 135.19 ms

Request ID
e14cc29b-9646-4f57-92b0-9625aefa08cb

Now, from the result above, if I add the custom_jwt_verify_service in the readdir function’s starting point, like this, /opt/nodejs/node_modules/custom_jwt_verify_service , then I will get below result.

Test Event Name
temp

Response
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}

Function Logs
START RequestId: 2f450b73-9885-4840-9013-eb0e389c141b Version: $LATEST
2024-06-22T12:58:31.129Z 2f450b73-9885-4840-9013-eb0e389c141b INFO /opt/nodejs/node20/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task
2024-06-22T12:58:31.147Z 2f450b73-9885-4840-9013-eb0e389c141b INFO .DS_Store
2024-06-22T12:58:31.147Z 2f450b73-9885-4840-9013-eb0e389c141b INFO index.mjs <-- our cusotm modules entry point
2024-06-22T12:58:31.147Z 2f450b73-9885-4840-9013-eb0e389c141b INFO jwt-verify.service.mjs <-- our service class
2024-06-22T12:58:31.147Z 2f450b73-9885-4840-9013-eb0e389c141b INFO node_modules <-- npm packages used by our custom module
END RequestId: 2f450b73-9885-4840-9013-eb0e389c141b
REPORT RequestId: 2f450b73-9885-4840-9013-eb0e389c141b Duration: 19.95 ms Billed Duration: 20 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 132.97 ms

Request ID
2f450b73-9885-4840-9013-eb0e389c141b

Now that we know where our layer’s code resides, we can import it into our function, like this.

// import MUST include the file name as well
import {JwtVerifyService} from "/opt/nodejs/node_modules/custom_jwt_verify_service/index.mjs";



import fs from "fs";


async function readDirContent() {
return fs.readdir("/opt/nodejs/node_modules/custom_jwt_verify_service", (err, files) => {
files.forEach(file => {
console.log(file);
});
});
}

export const handler = async (event) => {
// console.log(process.env.NODE_PATH);

// await readDirContent();

const obj = new JwtVerifyService("some-url", "some-token");
console.log(obj);


// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};

With the result below:

Test Event Name
temp

Response
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}

Function Logs
START RequestId: d99271f3-3adf-484f-8ad8-e376ef5bbcfc Version: $LATEST
2024-06-22T13:07:17.970Z d99271f3-3adf-484f-8ad8-e376ef5bbcfc INFO JwtVerifyService { jwksUrl: 'some-url', jwtToken: 'some-token' } <-- our service class instance
END RequestId: d99271f3-3adf-484f-8ad8-e376ef5bbcfc
REPORT RequestId: d99271f3-3adf-484f-8ad8-e376ef5bbcfc Duration: 72.83 ms Billed Duration: 73 ms Memory Size: 128 MB Max Memory Used: 84 MB Init Duration: 326.17 ms

Request ID
d99271f3-3adf-484f-8ad8-e376ef5bbcfc

That’s it. Now you know how to use a lambda layer inside a lambda function, and also, how to find out the location of the lambda layer code.

PS: Please check the results carefully as they contain some inline comments for ease of understanding.

Thanks for sticking with me.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response