Is it required to use async functions in koa framework? - node.js

I started learn koa.js and on koa documentation I found such demo code:
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
This middleware use async function, but code is sync. Is this required to use async function everywhere in koa or there is specific case for it?

Short answer :
You can use sync function as well, it will work perfectly.
Long answer :
Promise vs async
Async function can be replaced by synchronous functions returning Promise into a sync function (see http://2ality.com/2016/10/async-function-tips.html for more info)
Koa compose
Koa is using koa-compose to handle middlewares.
If you check compose function in
https://github.com/koajs/compose/blob/master/index.js
Code is
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1);
}))
Your middleware function fn is then bound into Promise.resolve which means that the output will be considered as a Promise even if you return non-Promise.
Sync Promise
It's not documented anywhere and would suggest you to avoid using undocumented patterns and I would do :
app.use(function(context){
// ...
return Promise.resolve();
})
As a sync design compatible with async.

Related

Express middleware cannot trap errors thrown by async/await, but why?

These two middleware functions behave differently and I cannot figure out why:
Here, the error will get trapped by try/catch:
router.get('/force_async_error/0', async function (req, res, next) {
try{
await Promise.reject(new Error('my zoom 0'));
}
catch(err){
next(err);
}
});
But here, the error will not get trapped by try/catch:
router.get('/force_async_error/1', async function (req, res, next) {
await Promise.reject(new Error('my zoom 1'));
});
I thought Express wrapped all middleware functions with try/catch, so I don't see how it would behave differently?
I looked into the Express source, and the handler looks like:
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next); // shouldn't this trap the async/await error?
} catch (err) {
next(err);
}
};
so why doesn't the try/catch there capture the thrown error?
I'm going to add an answer here even though you've already accepted another one because I think what's going on here can be explained better and this will help others attempting to understand this.
In your code here:
router.get('/force_async_error/1', async function (req, res, next) {
await Promise.reject(new Error('my zoom 1'));
});
Let's discuss what is going on:
First, you declared the callback as async which you had to do in order to use await in it. An async function tells the interpreter to do several important things.
1. An async function always returns a promise. The resolved value of the promise will be whatever the function returns.
2. An async function is internally wrapped with a try/catch. If any exceptions are thrown in the top level scope of the function code, then those exceptions will be caught and will automatically reject the promise that the function returns.
3. An async function allows you to use await. This is an indicator to the interpreter that it should implement and allow the await syntax inside the function. This is tied to the previous two points above which is why you can't use await in just any 'ol function. Any uncaught rejections from await will also reject the promise that the function returns.
It's important to understand that while the async/await syntax allows you to kind of program with exceptions and try/catch like synchronous code, it isn't exactly the same thing. The function is still returning a promise immediately and uncaught exceptions in the function cause that promise to get rejected at some time later. They don't cause a synchronous exception to bubble up to the caller. So, the Express try/catch won't see a synchronous exception.
But here, the error will not get trapped by try/catch
I thought Express wrapped all middleware functions with try/catch, so I don't see how it would behave differently?
so why doesn't the try/catch [in Express] there capture the thrown error?
This is for two reasons:
The rejected promise is not a synchronous throw so there's no way for Express to catch it with a try/catch. The function just returns a rejected promise.
Express is not looking at the return value of the route handler callback at all (you can see that in the Express code you show). So, the fact that your async function returns a promise which is later rejected is just completely ignored by Express. It just does this fn(req, res, next); and does not pay attention to the returned promise. Thus the rejection of the promise falls on deaf ears.
There is a somewhat Express-like framework called Koa that uses promises a lot and does pay attention to returned promises and which would see your rejected promise. But, that's not what Express does.
If you wanted some Koa-type functionality in Express, you could implement it yourself. In order to leave other functionality undisturbed so it can work normally, I'll implement a new method called getAsync that does use promises:
router.getAsync = function(...args) {
let fn = args.pop();
// replace route with our own route wrapper
args.push(function(req, res, next) {
let p = fn(req, res, next);
// if it looks like a promise was returned here
if (p && typeof p.catch === "function") {
p.catch(err => {
next(err);
});
}
});
return router.get(...args);
}
You could then do this:
router.getAsync('/force_async_error/1', async function (req, res, next) {
await Promise.reject(new Error('my zoom 1'));
});
And, it would properly call next(err) with your error.
Or, your code could even just be this:
router.getAsync('/force_async_error/1', function (req, res, next) {
return Promise.reject(new Error('my zoom 1'));
});
P.S. In a full implementation, you'd probably make async versions of a bunch of the verbs and you'd implement it for middleware and you'd put it on the router prototype. But, this example is to show you how that could work, not to do a full implementation here.
This is because the call is asynchronous, take this code :
try {
console.log('Before setTimeout')
setTimeout(() => {
throw new Error('Oups')
})
console.log('After setTimeout')
}
catch(err) {
console.log('Caught', err)
}
console.log("Point of non-return, I can't handle anything anymore")
If you run it you should see that the error is triggered after Point of non-return.
When we're at the throw line it's too late, we're outside of try/catch. At this moment if an error is thrown it'll be uncaught.
You can work around this by using async/await in the caller (doesn't matter for the callee), ie :
void async function () {
try {
console.log('Before setTimeout')
await new Promise((resolve, reject) =>
setTimeout(() => {
reject(new Error('Oups'))
})
)
console.log('After setTimeout')
}
catch(err) {
console.log('Caught', err.stack)
}
console.log("Point of non-return, I can't handle anything anymore")
}()
Finally, this means that for Express to handle async errors you would need to change the code to :
async function handle(req, res, next) {
// [...]
try {
await fn(req, res, next); // shouldn't this trap the async/await error?
} catch (err) {
next(err);
}
}
A better workaround:
Define a wrap function like this :
const wrap = fn => (...args) => Promise
.resolve(fn(...args))
.catch(args[2])
And use it like this :
app.get('/', wrap(async () => {
await Promise.reject('It crashes!')
}))
Neither of these really answer the question, which if I understand correctly is:
Since the async/await syntax lets you handle rejected "awaits" with non-async style try/catch syntax, why doesn't a failed "await" get handled by Express' try/catch at the top level and turned into a 500 for you?
I believe the answer is that whatever function in the Express internals that calls you would also have to be declared with "async" and invoke your handler with "await" to enable async-catching try/catch to work at that level.
Wonder if there's a feature request for the Express team? All they'd need to add is two keywords in two places. If success, do nothing, if exception hand off to the error handling stack.
Beware that if you don't await or return the promise, it has nothing to do with express - it just crashes the whole process.
For a general solution for detached promise rejections:
https://stackoverflow.com/a/28709667
Copied from above answer:
process.on("unhandledRejection", function(reason, p){
console.log("Unhandled", reason, p); // log all your errors, "unsuppressing" them.
//throw reason; // optional, in case you want to treat these as errors
});

Get return values from firebase functions?

I'm trying to get json from a firebase function request, here's what I'm trying:
export const listener = functions.https.onRequest(async (req, res) => {
return {foo:"bar"}
})
unfortunately this yields a timeout with no result when I go to the appropriate url in chrome, I also tried:
function getDude(){
return {dude:"dude"};
}
export const listener = functions.https.onRequest(async (req, res) => {
return Promise.all([getDude()]);
})
Same result as before.
HTTPS type functions don't return promises, so you should not declare them async. (However, all of the other types of Cloud Functions do require that you return promises for any async work they perform.)
HTTPS type functions are obliged to return a result to the client in order to terminate the function. This is as simple as using res.send(), or any of the methods described in the documentation. That's not to say you shouldn't be using promises in the function to wait for async work to complete - you just don't return one from the function.

Koa send status 404 every time is

export async function getPlaces(ctx, next) {
const { error, data } = await PlaceModel.getPlaces(ctx.query);
console.log(error, data);
if (error) {
return ctx.throw(422, error);
}
ctx.body = data;
}
Koa everytime sends 404 status and empty body, what i'm doing wrong ?
In seems that, await does not really "wait" and therefore returns too early (this results in a 404 error).
One reason for that could be that your PlaceModel.getPlaces(ctx.query) does not returns a promise. So it continues without waiting on results from getPlaces.
I also had this issue, and resolved it by adding :
ctx.status = 200;
directly below
ctx.body = data;
You've got to wire up your function with the router. Here's a little example how it works:
import * as Koa from "koa";
import * as Router from "koa-router";
let app = new Koa();
let router = new Router();
async function ping(ctx) {
ctx.body = "pong";
ctx.status = 200;
}
router.get("/ping", ping);
app.use(router.routes());
app.listen(8080);
in my case while using koa router i had forgotten to add
app.use(router.routes())
just above
app.listen(port)
Maybe you have middleware that is being triggered before this async function, but that middleware isn't async.
If you have any non-async middleware that gets called before this function, convert it to async.
Instead of calling next(); in the async middleware, call await next(); even if it is the last line of the middleware function.

How to access knex query results

Using knex with express, how can I access the results of a knex query?
Example:
var bots = []
response = knex.select('id', 'name').from('robots')
.then(function(robots){
console.log(robots);
bots = robots
});
console.log(bots)
This will log the robots but not not update the bots array, which is empty.
EDIT:
As a synchronous workaround, in an express route, I stuck the express block inside the knex block:
router.get('/robots', function (req, res) {
response = knex.select('id', 'name').from('robots').then(function(bots){
res.render('robots/index', {
page_title: 'All Robots',
robots: bots
}); // res.render
}); // knex.select
}); // router.get
Is this the recommended pattern?
knex uses Promises. Specifically, it uses http://bluebirdjs.com/docs/getting-started.html. console.log(bots) will not work because it is called right away, while .then( ... ) is only called after the knex query has been successfully called and run.
Your edited, "synchronous workaround" is the correct method to run the Express response, though you do not need to set that query to var response (see my comment on your question for more on that).
I'd suggest using async/await functions.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
router.get('/robots', async function (req, res) {
const bots = await knex.select('id', 'name').from('robots');
res.render('robots/index', {
page_title: 'All Robots',
robots: bots
}); // res.render
}); // router.get

Unit testing promise based code in node.js express route/controller

Recently I switched from using callbacks to using promise in my rest api express app.
But I'm having trouble with unit testing routes/controller with async behaviour of the promise. Here is the sample code that needs to be unit tested.
var handler = function (req, res, next) {
var query = {},
var options = {
sort: { updatedAt: -1 },
limit: 10
};
if (req.query.before) {
query.updatedAt = { $lt: req.query.before };
}
// User.findAsync returns bluebird promise
User.findAsync(query, null, options).then(function (user) {
res.json(user);
}).catch(function (e) {
next(e);
});
}
router.get('/api/users', handler);
My approach to test above code was to spy on req, next, and User.findAsync and check if they are called with correct arguments. But because of async behaviour of the promise, I was having trouble to check if res.json or next are get called.
I've tried to stub findAsync to return resolved promise (Promise.resolve(user)). but still then callback is executed asynchronously.
I'm not sure if I'm on the right track for testing express application.
What is good strategy to test this kind of code in good separation?
I've also heard about using supertest.
But for me, Using supertest to test from http end point feels like more of integration testing which is not unit testing and is quite expensive.
Also, In general, I would like to know if it is good practice to try to cover all of the code with unit testing (models, controller, middleware, etc) and what's good strategies or techniques of doing that. Or If it is just good enough to test http end points with super test.
If your method being tested doesn't return a promise then you can't use the promise syntax in Mocha. You can test your method the same way you'd test any other asynchronous method - with done as a parameter of it. Let's say we want to test your handler function:
var handler = function (req, res, next) {
//...
User.findAsync(query, null, options).then(function (user) {
res.json(user);
}).catch(function (e) {
next(e);
});
}
We can write a test as such:
describe("The handler", function(){
it("calls res.json", function(done){ // note the done argument
handler({query: {before: 5}, // mock request
{json: done} // res.json calls our `done` param of this function
function(){ throw new Error("error called"); });
});
});
Note that we mocked the request, response and the next handler. Our mocked response has a json method that lets the test know it is complete (this can be a function if you want to make assertions inside it) and if next is called instead we throw to signal it's not something that was supposed to happen.

Resources