dougmoscrop / serverless-http

Use your existing middleware framework (e.g. Express, Koa) in AWS Lambda 🎉
Other
1.72k stars 165 forks source link

res.on("finish", ()=>{}) middlewares can't be async #161

Closed jlitowitz closed 4 years ago

jlitowitz commented 4 years ago

I'm trying to use res.on("finish", ()=>{}) to handle logging after the request has been served. Everything works great, except if the logging handler has an async call, the async part never seems to execute. In other words, something like the following:

router.use((req, res, next) => {
    res.on("finish", () => {
            console.log('reaches here');
            docClient.put(params, function(err, data) {
                        console.log('never reaches here');
            });
    })
    next();
})

I assume it has something to do with the underlying lambda not waiting for the event queue to be empty, but async calls during the actual Express routes work fine, so unsure how to proceed. I've tried wrapping it in a Promise, using async/await, etc... but no luck. Not sure if it's specific to the res.on callback or middlewares in particular, haven't investigated that. Any help would be appreciated!

dougmoscrop commented 4 years ago

I don't think this is necessary specific to serverless-http, I think that this would happen even with regular Lambda stuff - once you return control to Lambda (by a resolved promise, etc.) all bets are off. Years ago when you called callback() in Lambda instead of returning a promise, it (by default) waited for an empty event loop - so your async thing would happen, but this also delayed the response, which I don't think you want either.

Ultimately I think this is unsupported in Lambda - I've seen this: https://xebia.com/blog/how-to-reduce-aws-lambda-latency-using-custom-runtimes/ but I'd be cautious that it's heading in to undefined behavior territory.

If you are OK with delaying your responses, there are maybe ways to do that, for example this library supports a response-transform hook, you don't have to do the transform, but it can be async and just do the logging only, leave the response alone. The only difference is it runs after the 'web framework' but before the response is handed back to Lambda.

jlitowitz commented 4 years ago

Thank you. I was unaware of the transform hook. That may work. Will give it a try and be happy to close this if it works.

jlitowitz commented 4 years ago

Ok, yep, that did it. I had to do a little finagling (since for my particular case I didn't want to log all requests), but all data I needed was there. Thanks!