Higher Order Functions with Express Routes - node.js

I was looking at a blog https://www.acuriousanimal.com/2018/02/15/express-async-middleware.html on how to handle do await/async in Typescript express routes.
I liked the idea of using the Higher Order Functions to avoid code redundancy, but
am not sure how to do it in TypeScript
Is there any other better way TypeScript recommends for this kind of scenario
Sample Code snippet:
https://gist.github.com/kameshsampath/e550d5bf19feb9b59d0ec1871e59b53a

I believe you want to remove the catch from the following code into a higher order function:
app.get("/api/frames", async (req: Request, res: Response, next: any) => {
//TODO move this to higher order function
try {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
} catch (err) {
res.status(404).send(err);
}
app.get("/api/frames", async (req: Request, res: Response, next: any) => {
//TODO move this to higher order function
try {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
} catch (err) {
res.status(404).send(err);
}
});
This does that:
const asyncHandler = fn => (req, res, next) =>
Promise
.resolve(fn(req, res, next))
.catch((err)=>res.status(404).send(err);)
app.get("/api/frames", asyncHandler(async (req: Request, res: Response, next: any) => {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
}));

Related

Express.js error handling function for async functions

How do I make express.js handle ERR_UNHANDLED_REJECTION in async route handlers? For example, how do I make it so that the code below doesn't crash on request:
import express, {NextFunction, Request, Response} from "express";
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', async () => {
await fails();
});
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({err})
});
app.listen(9999);
Try Koa as it's already async.
Otherwise, create a generic wrapper function that can turn an async function into the required express response.
import express, {NextFunction, Request, Response} from "express";
function genHandler(responseHandler: Function) {
return async function theHandler(req: Request, res: Response, next: NextFunction) {
try {
const res = await responseHandler(req, res, next)
res.send(res)
}
catch (error) {
next(error)
}
}
}
async function ok() {
return 'ok'
}
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', genHandler(fails));
app.get('/ok', genHandler(ok));
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({err})
});
app.listen(9999);
The handler generator can hide a lot of repeated async request/response complexity, like express middleware does for the non async world.
You can use try-catch in you route.
import express, {
NextFunction,
Request,
Response
} from "express";
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', async() => {
try {
await fails();
} catch (error) {
// You catch the error in here
}
});
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({
err
})
});
app.listen(9999);
you must use try and catch block.
import express, {NextFunction, Request, Response} from "express";
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', async (req, res, next){
try{
await fails();
}
catch(err){
//you can also create a global function to handle errors
return res.send(err)
}
});
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({err})
});
app.listen(9999);

How to use result of called 'then' for next 'then'?

I want to return what I get after an async call.
So
app.get("/books", async (req, res) => {
let books = await getBooks()
.then(json => {
res.status(200).send({"books": json});
});
});
Should wait on rendering the result until the called getBooks is done.
export async function getBooks() {
console.log("Getting books from cloud");
Book.findAll({
// ...
}).then(books => {
console.log("Got books");
return JSON.stringify(books, null, 4);
});
}
But right now the response gets rendered without actually waiting for the result.
You don't need to use promises. You can just use await and then use the result of that.
app.get("/books", async (req, res) => {
const books = await getBooks();
res.status(200).send({ books });
});
I'd highly suggest taking it a step further and using try/catch to handle failure cases
app.get("/books", async (req, res) => {
try {
const books = await getBooks();
res.status(200).send({ books });
} catch (error) {
// massage this to send the correct status code and body
res.status(400).send( { error });
}
});
You can use await in second method too:
export async function getBooks() {
console.log("Getting books from cloud");
var books = await Book.findAll({
// ...
})
if(books){
console.log("Got books");
return JSON.stringify(books, null, 4);
}
You just need to return your promise, and since you're just returning a promise you don't need async;.
app.get("/books", async (req, res) => {
let json = await getBooks()
res.status(200).send({"books": json});
});
export function getBooks() {
console.log("Getting books from cloud");
return Book.findAll({
// ...
}).then(books => {
console.log("Got books");
return JSON.stringify(books, null, 4);
});
}
app.get("/books", async (req, res) => {
let books = await getBooks();
res.status(200).send({"books": books});
})

Best way to handle mixed synchronous and asynchronous errors in ExpressJS

Using NodeJS 10.13.0, ExpressJS 4.16.4...
Got a controller handling a route that looks like this:
import { Request, Response, NextFunction } from 'express';
import braintree from 'braintree';
import config from '../../server.config';
export function index(req: Request, res: Response, next: NextFunction): void {
if(!config.braintree) throw new Error('Braintree configuration missing.');
const gateway: any = braintree.connect(config.braintree); // synchronous
return gateway.clientToken.generate({})
.then(response => res.send(response.clientToken))
.catch(next) // operational error unless config.braintree was changed
}
Reading the ExpressJS docs on error handling, I'm wondering if I'm following best practices - throwing an Error for the synchronous portion and passing the error to next() in the catch for the asynchronous portion.
Any recommendations for improvement?
Considering that promises are in use, both synchronous and asynchronous errors can be consistently handled with async function:
export async function index(req: Request, res: Response, next: NextFunction) {
try {
if(!config.braintree)
throw new Error('Braintree configuration missing.');
const gateway: any = braintree.connect(config.braintree);
const response = await gateway.clientToken.generate({})
res.send(response.clientToken);
} catch (err) {
next(err);
}
}
Since Express doesn't support promises, async function body should be wrapped with try..catch. Considering that try..catch is common for all async middleware functions, it could be moved to a helper:
const asyncMiddleware = (middleware: RequestHandler) => async (req: Request, res: Response, next: NextFunction) => {
try {
await middleware(req, res, next);
} catch (err) {
next(err)
}
};
Which is used like:
export const index = asyncMiddleware(async (...) => {...});

How to avoid redundant code writing promises?

I have a file where multiple export functions that are invoked from api's and each of these methods will make some get/post inside the function.
so my questions is for Promise.all that looks redundant to me is there better approach to achieve this using one private method handler that can be implemented or invoked from each of those export function and return response.
main.ts
export function getUser(req: Request, res: Response) {
const p1 = Promise.resolve("data1");
const p2 = Promise.resolve("data2");
Promise.all([p1,p2])
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
export function getRanks(req: Request, res: Response) {
const p1 = Promise.resolve("data3");
const p2 = Promise.resolve("data4");
Promise.all([p1,p2])
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
You can do exactly what you wrote - create function that does the general handling.
export function getUser(req: Request, res: Response) {
const p1 = Promise.resolve("data1");
const p2 = Promise.resolve("data2");
sendResponse(req, res, [p1,p2]);
}
export function getRanks(req: Request, res: Response) {
const p1 = Promise.resolve("data3");
const p2 = Promise.resolve("data4");
sendResponse(req, res, [p1,p2]);
}
function sendResponse(req, res, promises) {
Promise.all(promises)
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
PS: You should have some res handling in .catch (res.end() or res.status(500); res.json({error: e})) otherwise the request will be hanging on for 30-90sec (based on your settings)
In case p1, etc. promises are really created Promise.resolve, it could be omitted; Promise.all accepts regular values.
It could be written with async..await in more succinct manner:
export async function getUser(req: Request, res: Response) {
...
try {
const results = await Promise.all([p1, p2]);
res.json(results);
} catch (e) {
console.log(e)
}
}
At this point functions don't need to be DRYed any further.

Nodejs - Async/Await to my controller

Right now i have this route controller
export let remove = (req: Request, res: Response) => {
Area.removeAndRecalc(req.query.ids).then(() => {
return res.json({ success: true });
});
};
and calls the following model method
areaSchema.statics.removeAndRecalc = async function(ids) {
let parents = await this.model("Area").find({_id: ids});
await this.model("Area").delete(
{
_id: ids
}
);
await parents.forEach((parent) => {
parent.recalcParentChilds();
})
return;
}
The function returns a promise. Is it possible to write this code inside the controller? I try to use "async" to my controller but i doesn't work
Something like this (doesn't work)
export let remove = async (req: Request, res: Response) => {
let parents = await this.model("Area").find({_id: req.query.ids});
await this.model("Area").delete(
{
_id: req.query.ids
}
);
await parents.forEach((parent) => {
parent.recalcParentChilds();
})
return res.json({ success: true });
};
It's not clear "what" exactly doesn't work. An exception or description of the bad behaviour would be useful.
But check out async-middleware. I'm using it like this, for example in some projects:
import { wrap } from 'async-middleware'
//Loads of code, some of which creates router, an Express router.
router.get('/some/path/here', wrap(async (req, res) {
var data = await dataFromService();
res.write(renderData(data));
res.end();
}));
//Loads of code
I think there is a mistake with forEach in the remove function , forEach returns undefined not a promise so await won't work as expected, try to something like this :
export let remove = async (req: Request, res: Response) => {
//...
for (let i=0; i< parent.length;i++){ // <-- use for loop instead of forEach
await parent.recalcParentChilds();
}
return res.json({ success: true });
};

Resources