Node.js serverless offline hangs after 1st request - node.js

first time when i make a request its working fine, but when i make the request again without restarting the server, its going time out and returning some promise error
following is the code and error
module.exports.getOne = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
db.once('open', () => {
Client.findOne({name:{ $regex : new RegExp(event.pathParameters.name, "i") }})
.then(client => callback(null, {
statusCode: 200,
body: JSON.stringify(client)
}))
.catch(err => callback(null, {
statusCode: err.statusCode || 500,
headers: { 'Content-Type': 'text/plain' },
body: 'Could not fetch the note.'
}))
.finally(() => {
// Close db connection or node event loop won't exit , and lambda will timeout
db.close();
});
});
};
Error:
(node:3273) UnhandledPromiseRejectionWarning: Error: Cannot stop server while in stopping state

The issue is that you aren't completing the call by calling callback so it thinks that your getOne() function isn't finished.
Take a look at the sample code that serverless provides when you setup a new serverless app with their template
module.exports.hello = (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: event,
}),
};
callback(null, response);
};

Related

Invoke lambda instances from lambda and wait for the longest to comple

I have a lambda and I want to invoke multiple lambdas instance and wait for the longest to complete.
I already checked online and found very interesting threads but none of them helped me achieve what I want.
Here's the code:
Lambda as promise
invokeVideoRenderer = (params) => {
const param = {
FunctionName: 'myFunction',
InvocationType: 'Event',
Payload: JSON.stringify({ body: params })
};
return new Promise(async (resolve, reject) => {
this.lambda.invoke(param, (error, data) => {
if (error) {
reject(error);
}
resolve(data); <--- it always returns { StatusCode: 202, Payload: '' }
});
});
};
the loop where I invoke the different lambdas
const promises = [...Array(totalNumberOfLambdaToInvoke).keys()].map(
async (part: number) => {
const body = {
// body
};
const request = await invokeVideoRenderer(body);
return request;
}
);
const results = await Promise.all(promises); <--- here I want to wait for the longest to complete
The lambda I am invoking:
const myFunction = async (event, context, callback) => {
try {
//...code
callback(null, {
status: 200,
body: JSON.stringify({
status: "SUCCESS",
}),
});
return;
} catch (error) {
callback(null, {
status: 500,
body: JSON.stringify({
status: "FAILED",
errorMessage: error,
}),
});
return;
}
};
What am I doing wrong?
An InvocationType of Event tells Lambda to invoke the child Lambda asynchronously. Hence the Lambda service responds with 202 (Accepted) response immediately. Meanwhile the child Lambda function executes.
If you want it to execute synchronously, then don't provide an InvocationType of Event. Either remove it entirely to get the default, or explicitly set it to RequestResponse (which is the default).
Note: it's typically not ideal to invoke Lambda functions synchronously if you can avoid it, because you are paying for both Lambdas concurrently.

Issue getting simple Fetch working in Netlify Functions

I've been working through the workshops for Netlify functions and have stumbled getting a simple Fetch response to work. When running the sample at:
https://github.com/DavidWells/netlify-functions-workshop/blob/master/lessons-code-complete/use-cases/5-fetching-data/functions/node-fetch/node-fetch.js
const fetch = require('node-fetch')
const API_ENDPOINT = 'https://cat-fact.herokuapp.com/facts'
exports.handler = async (event, context) => { let response
try {
response = await fetch(API_ENDPOINT)
// handle response
} catch (err) {
return {
statusCode: err.statusCode || 500,
body: JSON.stringify({
error: err.message
})
} }
return {
statusCode: 200,
body: JSON.stringify({
data: response
}) } }
but I just get the following response:
{"data":{"size":0,"timeout":0}}
The build process is working fine, and I've tried other end points but all give the same results.
Here is a working version of your original code using node-fetch and the callback.
// import fetch from 'node-fetch';
const fetch = require('node-fetch')
const checkStatus = (res) => {
if (res.ok) { // res.status >= 200 && res.status < 300
return res.json()
} else {
throw new Error(res.statusText);
}
}
exports.handler = async function(event, context, callback) {
try {
const response = await fetch('https://cat-fact.herokuapp.com/facts')
const data = await checkStatus(response)
callback(null, {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
} catch (error) {
callback(error)
}
}
Note: Netlify functions are just AWS functions and you can read about the AWS Lambda Function Handler in the AWS docs
For async functions, you return a response, error, or promise to the runtime instead of using callback.
I like to use the method recommended in the docs, since it is supported and save the callback for non-async functions.
Here is a working version returning a promise to the async function instead.
// import fetch from 'node-fetch';
const fetch = require('node-fetch')
exports.handler = async (event, context) => {
return new Promise((resolve, reject) => {
fetch('https://cat-fact.herokuapp.com/facts')
.then(res => {
if (res.ok) { // res.status >= 200 && res.status < 300
return res.json();
} else {
resolve({ statusCode: res.status || 500, body: res.statusText })
};
})
.then(data =>{
const response = {
statusCode: 200,
headers: { 'content-type': 'application/json' },
body: JSON.stringify(data)
}
resolve(response);
})
.catch(err => {
console.log(err)
resolve({ statusCode: err.statusCode || 500, body: err.message })
})
})
}
This is just an example and there are other ways to handle the errors, but this example resolved the response rather than rejecting. It is not tested as a production example, but gives you an idea of the difference.
Here are some examples of Netlify functions.
On the handle response line you’ll want to await the json result:
response = await response.json();
Check the MDN article for Using Fetch for more details.
In the end I switched to the Axios library and it worked first time:
const axios = require('axios')
const API_ENDPOINT = 'https://jsonplaceholder.typicode.com/todos/1'
exports.handler = async (event, context) => {
let response
try {
response = await axios.get(API_ENDPOINT)
} catch (err) {
return {
statusCode: err.statusCode || 500,
body: JSON.stringify({
error: err.message
})
}
}
return {
statusCode: 200,
body: JSON.stringify({
data: response.data
})
}
}

NodeJs npm requestretry package Not Work on AWS Lambda Nodejs8.10

Here is my nodejs8.10 test code:
var request = require('requestretry');
const testRequest = async() =>{
request({
url: 'https://jsonplaceholder.typicode.com/posts/1',
json: true,
maxAttempts: 5, // (default) try 5 times
retryDelay: 5000, // (default) wait for 5s before trying again
retryStrategy: request.RetryStrategies.HTTPOrNetworkError
}, function(err, response, body){
if (response) {
console.log('The number of request attempts: ' + response.attempts);
console.log(body);
}else{
console.log('error: ', err);
}
});
}
testRequest(); // for local implementation
exports.handler = async (event) => {
let x = await testRequest();
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Test Lambda!'),
};
return response;
};
vs. nodejs6.10 test code:
var request = require('requestretry');
function testRequest () {
request({
url: 'https://jsonplaceholder.typicode.com/posts/1',
json: true,
// The below parameters are specific to request-retry
maxAttempts: 5, // (default) try 5 times
retryDelay: 5000, // (default) wait for 5s before trying again
retryStrategy: request.RetryStrategies.HTTPOrNetworkError // (default) retry on 5xx or network errors
}, function(err, response, body){
// this callback will only be called when the request succeeded or after maxAttempts or on error
if (response) {
console.log(body);
}
});
}
testRequest();
exports.handler = (event, context, callback) => {
testRequest();
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
callback(null, response);
};
It won't display the log that related to requestretry package on lambda Nodejs 8.10 and no any error messages. However, it can get the response and logs by running with command node <filename> in the local machine after npm install with the necessary packages like 'requestretry' and its required packages. If the above file converted into Nodejs6.10 format and it won't have the issue on both aws lambda and local machine.
I need to find out the causing before I convert the codes from nodejs6.10 (callback format)to nodejs8.10 (async format).

Using a callback inside of AWS Lambda handler

I've been struggling with trying to output the success/error of my promise chain.
I am chaining promises together like this
exports.handler = function(event, context, callback) {
Q.allSettled(init()).then(function (result) {
requestValid(event.queryStringParameters)
.then(isDuplicate)
.then(process)
.then(insertData)
.then(displayResponse)
.catch(function (error) {
// Handle any error from all above steps
console.error(
'Error: ' + error
);
callback({
statusCode: 500,
body: JSON.stringify({
message: error
}, null)
});
})
.done(function () {
console.log(
'Script Finished'
);
callback(null, {
statusCode: 200,
body: JSON.stringify({
message: 'Done'
})
});
});
});
};
I am calling Q.defer.reject(error_message); on fail and Q.defer.resolve(success_message) on success inside of the promises. If any of these promises fail, the error is being caught in .catch(function (error) {.
That's all fine but how do I return this data to the handler's callback?
Example of what I want to do, but don't how/where to put it because of the promises...
exports.handler = function (event, context, callback) {
let response = {
statusCode: 200,
body: JSON.stringify('some success or error')
};
// Return all of this back to the caller (aws lambda)
callback(null, response);
};
Thank you in advance..
Chain your promises all the way you usually do it. Return the result on the last step of the chain. catch errors at the end, and return the error if any:
exports.handler = function(event, context, callback) {
RequestIsValid(event)
.then(isDuplicate)
.then(process)
.then(insertData)
.then(getResponse)
.then(function(result) {
//result would be the returning value of getResponse.
callback(null, {
statusCode: 200,
body: JSON.stringify({
message: 'Done',
response: result
})
});
})
.catch(function (error) {
// this will show on CloudWatch
console.error(
'Error: ' + error
);
// this will be the response for the client
callback({
statusCode: 500,
body: JSON.stringify({
message: error
}, null)
});
})
// end of the function
}
That's assuming that isDuplicate, process, insertData and getResposne return a promise and the result of each can be chained to the next one.

Sub callback in NodeJS for AWS Lambda

How can my AWS Lambda make an API call to some external service and return the output to the user who invoked the function please?
My piece of code looks like this:
module.exports.oauth_callback = (event, context, callback) => {
oauth2.authorizationCode.getToken({
redirect_uri: 'https://' + event.headers.Host + event.requestContext.path
}).then(results => {
callback(null, {statusCode: 200, body: JSON.stringify(results)});
});
};
With this, I receive a 500 Internal Error because I believe the lambda function is exiting before returning the result.
EDIT Case number 2:
Actually in the case where I add another callback like this:
return oauth2.authorizationCode.getToken({
code: code,
}).then(results => {
createFirebaseAccount(results).then(token => {
const template = signInFirebaseTemplate(token);
return callback(null, { statusCode: 200, body: template.toString() });
});
});
};
My variable template has its value but the next callback never returns and my function times out
I think you might need to return your promise from getToken(), like this:
module.exports.oauth_callback = (event, context, callback) => {
return oauth2.authorizationCode.getToken({
redirect_uri: 'https://' + event.headers.Host + event.requestContext.path
}).then(results => {
callback(null, {statusCode: 200, body: JSON.stringify(results)});
});
};
If that doesn't work, check your CloudWatch log under the monitoring tab of your Lambda.

Resources