lambda-api
lambda-api copied to clipboard
Async operation before send
Hello everyone!
I need to save some important data every time when the response is already available.
I have tried 2 approaches:
-
api.finally
- does not allow us to use an async callback.
api.finally(async (req, res) => {
await saveAudit(req, res)
res.cors()
console.log('done')
})
- Tried to override the method
res.send
in a middleware - it looks like the send method can not include any async operations.
function auditHandler (handler) {
return (req, res, next) => {
res.sendRes= res.send
res.send = function (body) {
const data = {}
saveAudit(data)
.finally(() => {
console.log('finally', resData)
res.sendRes.call(this, body)
})
}
next()
}
}
As a result - the API does not work, but it works if we do not wait for an asynchronous operation.
Interesting topic, I was thinking for something like middleware but then before the send
My use-case was that I would like to create a function for cache headers depending of the status code For that you need access to the response details, like statusCode, current headers etc
I have checked the source code and see why it does not work
The api.finally
supports async callback
But the method res.send
does not wait for the async method _callback
It works if we do the next changes:
2.
There is a way how to do some async
operations in finally.
- Use
callback
from lambda function
module.exports.handler = (event, context, callback) => {
api.run(event, context, callback)
}
- Use async operation in
api.finally
- !!! important - do not useasync / await
api.finally((req, res) => {
saveAudit().finally(() => { console.log('done') })
})
- Why it works:
The third argument,
callback
, is a function that you can call in non-async handlers to send a response. The callback function takes two arguments: an Error and a response.When you call it, Lambda waits for the event loop to be empty and then returns the response or error to the invoker.
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html
api.finally((req, res) ... works great
I see that in the handler you can also use then on run
return api.run(event, context)
.then(res => {
console.log(res);
do stuff...;
return res;
})
I too wish that finally
could take an async function. I put the MySQL pool (from, ahem, jeremydaly/serverless-mysql) into a request field, and finally
gets passed the request object, so it would be the better way to clean up afterward.
For now, instead, I keep the pool in a global variable and assign it to the request but clean up in the handler function:
const api = createAPI({logger: {access: true}});
api.use((req, res, next) => {
req.mysql = prepareMySQL();
next();
});
const func: APIGatewayProxyHandlerV2WithJWTAuthorizer = async
(event:APIGatewayProxyEventV2WithJWTAuthorizer, context:Context):
Promise<APIGatewayProxyStructuredResultV2> => {
const result = await api.run(event, context);
await cleanUpMySQL();
return result;
}
export default func;