Global try-catch in serverless app

Serverless is convenient to deploy your service, but it may not be so convenient to coding.

For example, when you create a serverless-app with serverless command line tool:

serverless create --template aws-nodejs --path my-service

You should get a serverless.yml file and a handler.js file.

Routers are defined in serverless.yml, with code like below:

functions:
hello:
handler: handler.hello

While handlers are defined in handler.js, with code like below:

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);
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
};

Every router has its own handler function, these functions didn’t have a global entry, so you cannot define a global try-catch this way. It seems that we have to define try-catch in every function to catch exceptions.


But javascript is a dynamic functional programming language, so we can create handler functions dynamical based on a common entry. Let me show you my solution:

Firstly, I have four http api(CRUD) in my service, below is the my routers in serverless.yml:

serverless.yml

So I need to create four functions(createUser、getUser、updateUser、deleteUser) in my handler.js.

And this is my handler.js file:

handler.js

Is there a function named createUser、getUser、updateUser or deleteUser? No! There’s only a generator function named run(which has a try-catch inside) and a required module named User.

What’s the trick?

The most important sentence in handler.js is:

module.exports[attr] = (event, context, callback) => {
co(run(event, context, callback, attr));
}

Whatever attr is , we have an exports function with the same name.

The generated handler function is a wrapper of *run() with additional parameter(ie. the function name). In generator function *run(), we have a try-catch, this is the actually global try-catch for all handler functions. It also wrappers a lambda-proxy response to the return value of actually handlers. So that you don’t need to write statusCode、header in every handler function.

So where on-earth is the actually handlers?

Yes, they are member functions of class User, let’s have a look at user.js:

user.js

Yeah, just define method in class User, then you can directly use it in serverless.yml as a handler.

This way, methods such as getUser just have to deal with data and return the actually result what you want client to get. Such as:

return {id: 1, name: 'James'};

With global exception catch mechanism, you do not have to catch errors in handler functions.

This example is quite simple, without params and cookie/header support. To get more details, you can watch on my serverless project user: