Node.js - How to Call External Function from AWS Lambda Index.js - node.js

I am very new to AWS Lambda and Node.js, so apologies for the elementary question. I have two JS files in my Lambda project's root directory:
index.js
engageMessage(); //For testing purposes - throws error
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('This is index'),
};
return response;
};
messenger.js
function engageMessage() {
console.log("Messenger, checking in!");
};
How do I call engageMessage() from my index.js file? Each time I try, I get a "TypeError: engageMessage is not a function" message, and am unsure of how to properly reference/import/require messenger.js.

In messenger.js you need to export your function
module.exports = { engageMessage }
Then require it in your index.js
const { engageMessage } = require("./messenger.js")

Related

#aws-sdk/client-lambda] - Invoke Lambda - Payload Response in Unit8Array - Convert to String

I am using the #aws-sdk/client-lambda npm package for invoking lambdas. I have two Lambdas. Lambda A & Lambda B. Lambda A is trying to invoke Lambda B.
Lambda A invokes Lambda B by running the following code:
const { LambdaClient, InvokeCommand } = require('#aws-sdk/client-lambda');
module.exports = {
getGitHubToken: async () => {
const client = new LambdaClient({ region: process.env.REGION });
const params = {
FunctionName: process.env.GITHUB_TOKEN_FUNCTION,
LogType: 'Tail',
Payload: '',
};
const command = new InvokeCommand(params);
try {
const { Payload } = await client.send(command);
console.log(Payload);
return Payload;
} catch (error) {
console.error(error.message);
throw error;
}
},
};
The expected response from Lambda B should look like this:
{
statusCode: 200,
body: JSON.stringify({
token: '123',
}),
};
However, Payload looks to be returning this from the line console.log(Payload);:
I looked on the AWS SDK Website and it looks like Payload returns a Uint8Array. I guess this is because it's from a promise?
I have tried doing Payload.toString() however that comes back as simply a string of the values in the Unit8Array. Example being:
2021-04-13T14:32:04.874Z worker:success Payload: 123,34,115,116,97,116,117,115,67,111,100,101,34,58,50,48,48,44,34,98,111,100,121,34,58,34,123,92,34,116,111,107,101,110,92,34,58,92,34,103,104,115,95,111,114,101,51,65,109,99,122,86,85,74,122,66,52,90,68,104,57,122,122,85,118,119,52,51,50,111,67,71,48,50,75,121,79,69,72,92,34,125,34,125
My Question:
How do I resolve data from Unit8Array to the data I was expecting from the Lambda response? Which is a JSON Object?
I have confirmed the requested Lambda (Lambda B in this case) is returning the data correctly by going to CloudWatch. Thanks.
Okay, I found a way to get this working.
You have to specify a text encoder:
const asciiDecoder = new TextDecoder('ascii');
Then decode it so it looks like this:
const data = asciiDecoder.decode(Payload);
I have logged an issue on their repository asking why this isn't included in the module. I will post an update on any movement on this.

Node function in AWS throws "missing ) after argument list" error

I have created a Lambda function (Node.js 12.x) in AWS so SNS messages are pushed to Slack from our ETL tool Matillion.
console.log('Loading function');
const https = require('https');
const url = require('url');
const slack_url = 'https://hooks.slack.com/services/T1MJBHQ95/B01DQ60NUR2/....';
const slack_req_opts = url.parse(slack_url);
slack_req_opts.method = 'POST';
slack_req_opts.headers = { 'Content-Type': 'application/json' };
exports.handler = function (event, context) {
(event.Records || []).forEach(function (rec) {
if (rec.Sns) {
var req = https.request(slack_req_opts, function (res) {
if (res.statusCode === 200) {
context.succeed('sns posted to slack');
}
else {
context.fail('status code: ' + res.statusCode);
}
});
req.on('error', function (e) {
console.log('problem with request: ' + e.message);
context.fail(e.message);
});
req.write(JSON.stringify({ text: `${rec.Sns.Message}` }));
req.end();
}
});
};
The function will fail with a missing ) after argument list syntax error. I run it thru a linter in Sublime and it throws an error on require and exports being undefined.
My research shows several challenges:
I may need a file called eslint.rc but I am unclear why I need to put
in.
The use of "require" and "exports" appears deprecated.
Can someone please give me pointers how what to focus on to resolve this? Thank you.
You made a syntax mistake in your code.
req.write(JSON.stringify({ text: `${rec.Sns.Message}` }));
req.end();
Need to add ) before ; in your req.write() statement.
Just use Axios, it will make your life easier:
console.log('Loading function');
const axios = require('axios');
const url = require('url');
const slack_url = 'https://hooks.slack.com/services/T1MJBHQ95/B01DQ60NUR2/....';
exports.handler = async (event, context) {
(event.Records || []).forEach(function(rec) {
if (rec.Sns) {
axios.post(slack_url, {
text: rec.Sns.Message
}, {
'Content-Type': 'application/json'
}).done(r => {
context.succeed('sns posted to slack');
}).catch(e => {
context.fail('status code: ' + e.statusCode);
});
});
Notice that in your code, after 1 sns event he end the invocation, not running all, to run all of them you need to wait for all requests to be done (can use Promise.all for example)
Also, be aware that sns can send directly to HTTPS without any code
https://aws.amazon.com/sns/features/
By the way, you can use an external monitoring tool that will show you exactly the outgoing request + all payload as Lumigo (As a disclaimer I work for a company named lumigo)
I fixed my issue:
I added the following statement at the top of my file /* eslint-env node, common */
I re-deployed my function.
I believe the redeployment of the function did the trick.

Why i get undefined in console?

I am new to aws and serverless .I don't know why i get undefined in the variable called - first:
When i execute the function the command
sls invoke local -f hello -d '{"first":2}'
The object
{
"first":2
}
is send to the serverless function.And it is in the response in the event property.
Why when i try to get the value of event.first i get undefined?
'use strict'
module.exports.hello = (event, context, callback) => {
const first = event.first;
console.log("**", first);
const response = {
statusCode: 200,
body: JSON.stringify({
event: event
}),
};
callback(null, response);
}
When i try event,first i need to get only the number in this case 2,but i get undefined

Sentry on AWS lambda in nodejs doesn't send exception

I'm trying to configure sentry on AWS Lambda (nodejs 8.10) but the exceptions are not sent to Sentry. I'm the feeling that is a issue of timing: the lambda is terminated before the data are sent to sentry.
Which is the right way to integrate sentry on AWS Lambda?
Thank you!
Update: Sentry automatically reports exceptions on Node/Lambda. see docs
You have to use the function flush which makes sure all events that are currently queued up will be sent:
Sentry.captureException(new Error('test'));
await Sentry.flush();
http://getsentry.github.io/sentry-javascript/modules/node.html#flush
Install the Sentry module
npm install #sentry/node
See the bellow example to configure AWS Lambda function with Sentry.
// Import the Sentry module.
const Sentry = require("#sentry/node");
// Configure the Sentry SDK.
Sentry.init({
dsn: "your dsn",
});
exports.handler = function (event, context) {
try {
textFunctionCall(); // Call undefined function.
} catch (e) {
Sentry.captureException(e); // using this function exception captured in Sentry dashboard.
Sentry.flush(2000);
}
const response = {
statusCode: 200,
body: "Hello, exception captured",
};
return response;
};
https://docs.sentry.io/platforms/node/serverless/#aws-lambda
Still not Capturing error in Sentry, then install the bellow node module these are the dependency.
npm install lru-cache
npm install lru_map
npm install tslib
Install sentry/node
npm i #sentry/node
Add this code in the top of your lambda
const Sentry = require("#sentry/node");
Sentry.init({
dsn: "https://0dc9558ffb934657b6c3b97182b4338d#sentry.io/182963" // your_dsn_key_here
});
Use sentry object
try {
//Logic here
} catch (error) {
Sentry.captureException("Error on getLogsById");
}
I try to work on this and got a solution using already existing function
Create file name sentry-lib.js and add below code
const Sentry = require("#sentry/node");
// Importing #sentry/tracing patches the global hub for tracing to work.
const SentryTracing = require("#sentry/tracing");
let transaction = null;
let SentryInstance = null;
export class SentryLib {
constructor(){
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});
}
startLogging(){
transaction = Sentry.startTransaction();
}
logError(error){
Sentry.captureException(error);
}
async endLogging(){
await Sentry.flush(); // Flush all awaiting event in queue
transaction.finish();
}
}
Now create file name second.js
export const sum = (){
const sentryLibIns = new SentryLib();
try{
// Something will break here
} catch(error){
sentryLibIns.error(error);
}
}
Now create the handler file name handler.js
const handler = async (event, context, callback)=>{
const sentryLibIns = new SentryLib();
sentryLibIns.startLogging();
try{
fn1();
} catch(error){
sentryLibIns.error(error);
} finally{
await sentryLibIns.endLogging();
}
}

Adding a new js file to my lambda function using Serverless

I'm trying to create an API on AWS Lambda, and using Serverless framework for deployment.
I'm a complete noob at it, so I'm not sure I'm doing the right thing. So here's the old favorite, hello world (index.js):
const serverless = require('serverless-http');
const express = require('express');
const app = express();
const Promise = require('promise');
app.get('/', function (req, res) {
res.send('Hello World!')
})
That works fine as is. But when I add the following:
const serverless = require('serverless-http');
const express = require('express')
const app = express()
const Register = require('./controller/registryController');
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.get('/createUserName', function (req, res) {
var prom = Register.createUser();
prom.then((message) => {
res.status(201).json(message);
})
.catch((message) => {
res.status(400).json(message);
});
})
module.exports.handler = serverless(app);
I get:
"Cannot find module './controller/registryController'"
Which is strange, cause the file is right there, when I look in lambda. Here's its contents:
const dbConnMysql = require('./dbMngrMysql');
var methods = {
createUser: function() {
return new Promise((resolve, reject) => {
let connection = dbConnMysql.createConnection();
dbConnMysql.startConnection(connection)
.then((fulfilled) => {
let table = 'userNamePool';
return dbConnMysql.selectFrom(connection, table, null);
})
.then((fulfilled) => {
return dbConnMysql.closeConnection(connection).then(function() {
let result = fulfilled;
let response = {
"statusCode": 200,
"headers": {"my_header": "my_value"},
"body": JSON.stringify(result),
"isBase64Encoded": false
};
resolve(response);
});
})
.catch((error) => {
let response = {
"statusCode": 404,
"headers": {"my_header": "my_value"},
"body": JSON.stringify(error),
"isBase64Encoded": false
};
resolve(response);
});
});
}
};
module.exports = methods;
Here's the project structure / directory:
And here's my serverless.yml for good measure:
service: sample-api
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: yapyap
deploymentBucket: yadayada
role: blahblahblah
functions:
app:
handler: index.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
createUserName:
handler: index.handler
events:
- http: 'GET /createUserName'
What am I doing wrong? Anything I can do to fix this?
There's a typo:
controller instead of controllers. 😊
Sorry, I can't comment because of the reputation restriction.
The problem may be because you haven't created controller folder.
To create it properly
create a folder. Name it controller. Create your registryController.js and put your code in it.
now you will be able to call it as
const Register = require('./controller/registryController');
Also on a side note, if you want to use a serverless architecture, separate your server side and client side code completely. Put your HTML files and client-side scripts in an S3 bucket. put your server-side code in lambda function and connect it with API Gateway. then use a JavaScript function to call the API. Keep server-side calls as less as possible.

Resources