The two types of Lambda handler functions
In the last post, I wrote about the arguments of the lambda handler function, which are event
and context
objects. But there is a third argument available, and this is the callback function, which is called when an error needs to be thrown or a value should be returned.
1. Node versions supported
AWS announced the end of support for Node.js version 6 a few weeks ago, and they now encourage users to upgrade to at least version 8. Since the referred AWS article was written, AWS has introduced support for version 10 as well.
But before that, when AWS supported Node.js version 6, it wasn’t possible to use async functions, because they are only available from version 7.7. Instead, the handler functions used a third argument, which was a callback function.
This structure of the handler function still exists, so currently we have two formats to write a handler function.
2. async handler functions
Since you can’t create a new lambda function with Node v6 as its runtime, the most straightforward way is to write it as an async
function.
async
lambda functions can do three things: return a result, throw an error or return a Promise if an asynchronous task needs to be performed.
2.1. Return a response
To return a response, writing a lambda handler is no different from creating a regular, well-behaved async
function.
In the following examples, the event
object will have a property called num
, which is a number, and the lambda function will multiply this number by 2. It happens that we have to multiply numbers by 2. I do it almost every day (although not exclusively in the code).
The handler in the module index
(that is, the name of the file containing the code is index.js
) can look like this:
exports.handler = async (event) => {
const numberToMultiply = event.num
try {
const result = await multiplyByTwo(numberToMultiply)
// we can do something with 'result' here, like add 1 to it
return result + 1
} catch (e) {
throw new Error(e)
}
}
function multiplyByTwo(num) {
return new Promise(function(res, rej) {
if (isNaN(num)) {
rej('Please enter a number')
}
res(num * 2)
})
}
Here we don’t use the context
argument, so it’s not necessary to include it in the function definition.
This is the traditional try...catch
block, which is frequently used with the async/await
syntax. Inside an async
function, we can await
multiplyByTwo
, and then the result can be return
ed. If we want or need to, we can do something with result
before returning a response.
2.2. Return a promise
With the sad path (in our case, when the type of the parameter is not a number) the promise is rejected, and we can throw an error.
The third option is to return the promise itself, which is very useful when the lambda function needs to interact with another AWS services, like S3 or DynamoDB. In our case, if we don’t want to do anything else but multiplying the number by 2, we can write something like this:
exports.handler = async (event) => {
return multiplyByTwo(event.num)
}
Lambda will send either the response or the error to the invoker depending on the promise resolving or rejecting.
3. Handler with callback
Some tutorials haven’t been updated yet, and they still contain a callback as the third argument for the handler function. This is not a big issue (only a bit inconvenient), and it will perfectly work with either version 8 or version 10. On the other hand, if, for some weird and unexpected reasons, you need to use callbacks (which shouldn’t be the case), the code can look like this:
exports.handler = function(event, context, callback) {
const numberToMultiply = event.num
multiplyByTwo(numberToMultiply)
.then(function(result) {
callback(null, `${ numberToMultiply } multiplied by 2 is ${ result }`)
})
.catch(callback)
}
callback
is a traditional error-first type callback function. Its first argument is the error, which is passed in inside the catch
block.
When everything goes well (inside then
), we want to pass in null
as the first argument (there’s no error here), and the second argument will be the value we would return
inside the async
version.
This is how the handler with callback relates to the async
type handler: The async
function should return what the callback function is called with as the second argument.
Using async
functions is easier and more elegant than callbacks, and I hope that I’m not alone with my bias. Either way, Lambda manages the invocation and throws the error if necessary.
4. Summary
Lambda handlers can come in two formats. One is the old but still supported callback, which is the third argument of the lambda function. It’s a classic error-first callback.
The second type is a newer async
function, which is supported from Node version 8.x runtime in AWS. This syntax is easier to write and understand.
Thanks for reading, and see you next time.