How can I use co with express? - node.js

I'm using express with node and want to use a co/yield patter to wrangle my async callbacks.
The current code looks like this:
web.post('/request/path', function(req, res, next) {
co(function *() {
let body = req.body
let account = yield db.get('account', {key: body.account})
if (!account) {
throw new Error('Cannot find account')
}
let host = yield db.get('host', {key: body.hostname})
....
}).catch(err => {log.info(err) ; res.send({error: err})})
This is working really well, but I'd like to be able to simplify the first 2 lines:
web.post('/request/path', function(req, res, next) {
co(function *() {
Is it possible to somehow integrate the co(function *() into the first line? Does express provide support for co() and yielding functions?

You can use co-express along with promises.
Example,
router.get('/', wrap(function* (req, res, next) {
var val;
try {
val = yield aPromise();
} catch (e) {
return next(e);
}
res.send(val);
}));

You can simplify the syntax with an arrow function:
web.post('/request/path', (req, res, next) => co(function *() {
//...
}).catch(err => {log.info(err) ; res.send({error: err})})
I don't see any extra benefit to using another package. When async/await hits the shelves, we may see express get an update.
On a side note, making your own co-express is rather simple:
Consider 'co-express/index.js'
module.exports = generator => (req, res, next) => require('co').wrap(generator)(req, res, next).catch(err => res.status(500).send(err));
And now:
var coe = require('./co-express');
web.post('/request/path', coe(function *(req, res, next) {
//...
})
This way you've got the latest co package.

Related

Nodejs Use a middleware inside another

I'm having some problems using 2 middlewares inside the same function, already tried to search for all internet and didn't find a useful solution.
validator file
module.exports = {
create: async (req, res, next) => {
await celebrate(options.create)(req, res, next);
return res.status(500).json({ message: 'middleware 2'});
},
}
routes file
routes.post('/user', UserValidator.Create ,UserController.create);
The celebrate lib filters some basic validations like string lenght, null values, etc. And the celebrate() function returns another function with the (req, res, next) params.
When the celebrate returns the validation error, it stills continues to execute the code, so it tries to execute the next return and I get an error because the return has already been sent.
When using separate middlewares in the routes, it works normally:
routes.post('/user', celebrate(...), middleware2 ,UserController.create);
I also tried this way but the same thing happens, but now without an error, just returning the middleware2 result.
module.exports = {
create: async (req, res, next) => {
await celebrate(options.create)(req, res, () => {
return res.status(500).json({ message: 'middleware 2'});
});
},
Is there a way to fix this?
u should try this structure
// API
app.post('/something', Middleware.validate, Controller.create)
//Middleware
const validate = (req, res, done) => {
const errorArray = []
const body = req.body
// identifier is required, Validating as String, and length range.
if (!_.isString(body.identifier) || body.identifier.length < 2 || body.identifier.length > 10) {
errorArray.push({
field: 'identifier',
error: 70000,
message: 'Please provide only valid \'identifier\' as string, length must be between 2 and 10.'
})
}
if (!_.isEmpty(errorArray)) {
return errorArray
}
done()
}
module.exports = {
validate
}
// Controller
const create = function (req, res) {
return // your functionality
}
module.exports = {
create
}

How to invoke multiple express middlewares manually

I have a situation where I want to invoke multiple express middlewares depends on the request payload.
These middlewares are generated from the express validator checkSchema
method.
So I have written a middleware which gets access to the request object and I can read a property from the request payload then take a decision on which schema has to be run.
An implementation would like this.
let app = express();
let schema1 = checkSchema({
field1: {
in: ["body"],
exists: {
errorMessage: "field1 is missing",
}
}
});
let schema2 = checkSchema({
field2: {
in: ["body"],
exists: {
errorMessage: "field 2 is missing",
}
}
});
app.post("/resource", (req, res, next) => {
if(req.body.type === "TYPE1") {
// Invoke schema1 middleware
}
if(req.body.type === "TYPE2") {
// Invoke schema2 middleware
}
});
Here schema1 and schema 2 are not single middleware. It is a
middleware array.
If it was middleware, I could call schema1(req, res, next);
If anybody has gone through this, Please suggest me what is the approach to run a middleware array manually.
I have released express-validator v6.0.0, which should help addressing this kind of thing.
Now there is a .run(req) method, which should let you do things with express-validator in an imperative way.
For your use case, you could do the following:
app.post("/resource", async (req, res, next) => {
if(req.body.type === "TYPE1") {
await Promise.all(schema1.map(chain => chain.run(req)));
}
if(req.body.type === "TYPE2") {
await Promise.all(schema2.map(chain => chain.run(req)));
}
});
Since checkSchema returns an array of validation chains, the code is mapping each of them to their respective execution promise.
When all of the promises are finished, your code can continue executing and do whatever you want. Maybe check if there are errors with validationResult, render a different page accordingly, etc -- up to you!
According to this question Use an array of middlewares at express.js there is one repo: https://github.com/blakeembrey/compose-middleware:
According to the readme:
app.use(compose([
function (req, res, next) {},
function (err, req, res, next) {},
function (req, res, next) {}
]))
So, what you can do is:
app.post("/resource", (req, res, next) => {
if(req.body.type === "TYPE1") {
compose(schema1)(req,res,next);
}
if(req.body.type === "TYPE2") {
compose(schema2)(req,res,next);
}
});

ExpressJS middleware that also catches errors?

I'm writing middleware that I'm applying at the route level, like so:
router.get('/foo', myMiddleware, (req, res) => { ... });
so I can do some stuff with the request. But I also need to catch errors to do some special handling. I know I can add a handler afterwards like this:
... (req, res) => { ... }, myErrorHandler);
and it'll get called just fine.
But my question is, is there any way to have a single piece of middleware that can do all of this so I don't need two points of integration? I tried calling req.on('error', (err) => { ... }) within my middleware but it never seems to be called.
Express comes with a built-in error handler that takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
// Router
router.get('/foo', myMiddleware, (req, res) => { ... });
// Router Error Handler
router.use(function (err, req, res, next) {
});
I ended up solving this by writing a helper function that wraps the actual handler. It looks like this:
function checkPage(handler: express.RequestHandler) {
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _write = res.write;
res.write = chunk => {
if (req.query.verbose) {
return _write.call(res, `<p>${chunk}</p>`);
} else {
return true;
}
}
try {
await handler(req, res, next);
res.write('<hr/><p style="color:green;"><b>happy</b></p>');
} catch (err) {
res.write(`<p style="color:red;">${err}</p>`);
res.write('<hr/><p style="color:red;"><b>SAD!</b></p>')
}
res.end();
}
}
Then in my route handler, I just use it like so:
router.get('/foo', checkPage(async (req, res, next) => {
...
res.write('stuff');
...
}));

Calling an API endpoint from within another route in Node / Express

I have myRoute.js with a route (GET) defined and I want to call an api endpoint from another route (api.js), and I'm not sure what the right way to do this is. The api.js route is working properly (image and code below).
api.js
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
works as expected:
myRoute.js
I would like when a user goes to localhost:3000/USER_ID that the route definition gets information from the api. Psuedo code below (someFunction).
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
let fromApi = someFunction(`localhost:3000/getAllGroups/${uid}`); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Not sure if i understand you correct but anyway i will try to help. So you have an api like
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
If you would like to reuse it you can extract a function from the code above like so:
async function getAllGroupsByUserId(uid){
const result = [];
try{
const data = await db.getAllGroups(uid);
for (i in data) {
result.push(data[i].groupname);
};
return result;
}
catch(e) {
return e;
}
}
And then reuse it in your api & anywhere you want:
router.get('/getGroups/:uid', async function(req, res, next) {
const uid = req.params.uid;
const groups = await getAllGroupsByUserId(uid);
res.status(200).send(groups);
})
Same you can do in your another route:
router.get('/:uid', async function(req, res, next) {
const uid = req.params.uid;
const fromApi = await getAllGroupsByUserId(uid); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Seems like pretty clear :)
I would use fetch for this. You can replace someFunction with fetch, and then put the res.render code in a .then(). So, you would get this:
const fetch = require("node-fetch");
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}').then(res => res.json()).then(function(data) {
returned = data.json();
console.log(returned); //expecting array
res.render('./personal/index.jade', {JSON.stringify(returned)});
});
});
A more robust way with error handling would be to write something like this:
const fetch = require("node-fetch");
function handleErrors(response) {
if(!response.ok) {
throw new Error("Request failed " + response.statusText);
}
return response;
}
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}')
.then(handleErrors)
.then(res => res.json())
.then(function(data) {
console.log(data) ; //expecting array
res.render('./personal/index.jade', {JSON.stringify(data)});
})
.catch(function(err) {
// handle the error here
})
});
The ideal way would be to abstract your code into a method so you aren't calling yourself, as The Reason said. However, if you really want to call yourself, this will work.

Express: Show value from external request as response

I have the following function where I am using the cryptocompare npm package:
getPrice: function(coin){
cc.price(coin, 'USD')
.then(prices => {
console.log(prices);
return prices;
}).catch(console.error)
}
// https://github.com/markusdanek/crypto-api/blob/master/server/helper/cryptocompare.js
Now I want to set up an Express server to open http://localhost:9000/current and to display the current "price".
So I have my controller which looks like this:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price);
}
};
// https://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.jshttps://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.js
My route:
var controllers = require('../controllers'),
app = require('express').Router();
module.exports = function(app) {
app.get('/current', controllers.crypto.getCurrentPrice);
};
When I open now http://localhost:9000/current I only get the current price in my console, but not in my browser.
How can I also set the response to the value?
I tried this but failed:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price){
res.status(200).json(price);
});
}
};
I guess thats the wrong way to call a callback.. do I have to modify my helper function or anything else?
My project is also on Github for further references: https://github.com/markusdanek/crypto-api
below may help you
module.exports = {
getCurrentPrice: function(req, res, next) {
cc.price('ETH', 'USD')
.then(prices => {
console.log(prices);
res.json(prices)
})
.catch(err=>{
console.error(err)
return next(err);
})
}
};

Resources