Express middleware to wrap response - node.js

I'm implementing an API where the result needs to be return wrapped by a result key, as such
{
result: [
{
id: 1,
name: "Bob"
}
]
}
What I'd like to do is add a piece of middleware (if possible) that does this wrapping to every response without having to think about it every time. What would be the best way to accomplish this? I could see modifying response.body and then calling next() instead of doing res.send(obj) (what I'm doing now).
Thanks!

I ended up extending the response object to add a new function (used like res.sendWrapped(data)) per In Express and Node.js, is it possible to extend or override methods of the response object?
as such:
express.response.sendWrapped = function(obj) {
return this.send({ result: obj });
};

Related

ExpressJS - function returning undefinded

function checkFamilyStatus() keeps returning undefined for some reason, when it should be returning a boolean value from a mongodb collection.
A bit of context here - I decided to separate the logic part from the router methods like get and post methods, so as to make it look clean and understandable like in functional programming. So I put the logic part in functions and invoke them inside the router methods, but it doesn't work out. Besides, I don't know if this is a good practice or not. Wouldn't it be much easier to read if done like this, instead of say, putting a whole bunch of code in one place?
I've been stuck on this piece of code for a while. I am a bit confused on the working of asynchronous JS as a whole. I did some research, but still don't understand why this wouldn't work. Could someone clarify this up for me?
// post method
router.post("/create", ensureAuthenticated, async(req, res) => {
let userID = req.user.id;
console.log(await checkFamilyStatus(userID)); // but returns undefined
// rest of the code
}
// check family status
checkFamilyStatus = async userID => {
Account.findOne({
_id: userID
}, (err, account) => {
return account.hasFamily; // should return boolean value ?
});
};
Assuming the logic behind account.hasFamily is correct, then you need to await for the return, so it should be return await account.hasFamily;

Recommended pattern to page through API response until exhausted?

I'm new to Node and the async programming model. I'm having problems dealing with a simple requirement that seems pretty basic in synchronous environments: paging through an API response until the response is empty.
More specifically, the API, on a successful call, will return data and a status of 200 or 206 (partial content). If I see the 206 response, I need to keep making calls to the API (also sending a page query param that I increment each time) until I see the 200 response.
In a synchronous language, the task will be a piece of cake:
// pseudocode
data = []
page = 1
do {
response = api.call(page)
data.append(response.data)
page++
} while (response != 200)
return data
Now, in Node, for a single api call, code like this will work:
// fire when '/' has a GET request
app.get('/', (req, res) => {
axios.get('https://api.com/v1/cats')
.then(response => {
// now what??
});
});
});
See the //now what?? comment? That's the point where I'm wondering how to proceed. I came across this somewhat-relevant post but am not able to convert this to a format that will work for me in Node and Axios.
Is it enough to just wrap the axios code in a separate function? I don't think so, because if I do this:
function getData(pageNum) {
axios.get('https://api.com/v1/cats')
.then(response => {
// now what??
});
});
}
I can't rely on a return value because as soon axios.get() gets executed, the function will be over. I can call getData() again after I get the first response, but then, suppose I want to return all the data from these multiple calls as the HTTP response from my Express server . . . how do I do that?
I hope I will not get downvoted for laziness or something. I've really looked around but not found anything relevant.
First, a counter-question: Is the data set so big that you need to worry about using up all the memory? Because if so then it will take more work to structure your code in a way that streams the data all the way through. (In fact I'm not even sure whether express allows streaming... you are using express aren't you?)
From the axios documentation, it looks like response is a readable stream which provides the response body. So reading it is also an asynchronous task. So you should write a function that does that. See the "Stream" page of the nodejs docs for more details. Or I could be persuaded to help with that too, time permitting. But for now, I'll assume you have a function readResponse, which takes an axios response object as an argument and returns a promise, and the promise resolves to an object such as { statusCode: 206, result: ['thing1', 'thing2'] }. I'll also assume that your goal is to get all the result arrays and concatenate them together to get e.g. ['thing1', 'thing2', 'thing3', 'thing4', 'thing5', 'thing6'].
You could write a self-calling version of your getData function. This will retrieve all data from a given page onwards (not just the page itself):
function getData(pageNum) {
axios.get('https://api.com/v1/cats' + (pageNum ? '?page=' + pageNum) : '')
.then(readResponse)
.then(function(parsedResponse) {
if(parsedResponse.statusCode == 200) {
return parsedResponse.result;
} else if(parsedResponse.statusCode == 206) {
return getData(pageNum + 1).then(function(laterData) {
return parsedResponse.result.concat(laterData);
});
} else {
// error handling here, throw an exception or return a failing promise.
}
});
});
}
Then, to get all data, just call this function with pageNum = 0:
// fire when '/' has a GET request
app.get('/', (req, res) => {
getData(0)
.then(function(results) {
// results is now the array you want.
var response = JSON.stringify(results); // or whatever you're doing to serialise your data
res.send(response);
});
});

How do I carry some data through Bluebirds .map?

I am using Blubird and Sequelize (which uses Blubird under the covers).
Suppose I have a code similar to:
Feed.findAll()
.map(function (feed) { // <---- this is what I'm interested in below
// do some stuff here
return some_promise_here;
})
.map(function (whatever) {
// What is the best way to access feed here?
})
....
I have found some replies which hinted at possible solutions, but I can't quite put my finger on it.
I have tried with Promise.all(), .spread(), but I never managed to make it work.
Feed.findAll()
.map(function (feed) { // <---- this is what I'm interested in below
// do some stuff here
return some_promise_here.then(function(result){
return { result: result, feed: feed};// return everything you need for the next promise map below.
});
})
.map(function (whatever) {
// here you are dealing with the mapped results from the previous .map
// whatever -> {result: [Object],feed:[Object]}
})
This looks very similar to How do I access previous promise results in a .then() chain?, however you're dealing with a .map call here and seem to want to access the previous result for the same index of the processed array. In that case, not all solutions do apply, and a closure seems to be the simplest solution:
Feed.findAll().map(function (feed) {
// do some stuff here
return some_promise_here.then(function (whatever) {
// access `feed` here
});
})
You can apply explicit pass-through as well, though, like outlined in #bluetoft's answer.

Passing an async object to dustjs template

I have a hard time understanding how to pass an object fetched from a database to a dust.js template.
Let's say I have a template:
{#person}
{name} - {title}
{/person}
I try to setup a context something like this:
var ctx = {
person: return chunk.map(function(chunk) {
database.person(12345, function(data) {
dust.nextTick(function() {
chunk.end(data); // What to really do here?
});
});
});
}
Where database.person fetches the object from a database and passes it to a callback.
And then I would run the render function:
res.render('person', ctx);
The correct form was:
var ctx = {
person: function(chunk, context, bodies) {
return chunk.map(function(chunk) {
database.person(1234, function(data) {
return chunk.render(bodies.block, context.push(data)).end();
});
});
}
}
Instead of write I had to call render as in Alan's answer.
The whole db call had to be enclosed in the return chunk.map call to work. I also had to chain an end command to send the results back to the stream.
Why these calls are needed is told in the dust.js guide (http://akdubya.github.io/dustjs/#guide):
chunk.map tells Dust to manufacture a new chunk, reserving a slot in
the output stream before continuing on to render the rest of the
template. You must (eventually) call chunk.end() on a mapped chunk to
weave its content back into the stream.
This is something that isn't addressed in the LinkedIn guide pages.
I'm assuming your db call will return something along the lines of a json object such as the "data" below.
The async code looks ok. chunk.write will just write out whatever you give it so you need to pass your data in on the context so it can be accessed in the template. Use chunk.render instead of write. A non-async example would be :
{
"person": function(chunk, context, bodies) {
var data = {name:"Smith", title:"Mr"};
return chunk.render(bodies.block, context.push(data));
}
}
Running that through linkedin dust tests seems to get the right answer.
Hope that helps,
Alan

How does Express/Connect middleware work?

I am learning Node.js, and I have read some tutorials, like The Node Beginner Book for learning the core funcionality. But the more I read some examples, the more doubts I start collecting.
On the further example, obtained from a tutorial, we can see that for a CRUD 'read' request for key /documents/titles.json, we are returning a value:
app.get('/documents/titles.json', loadUser, function(req, res) {
Document.find({ user_id: req.currentUser.id },[], { sort: ['title', 'descending'] },
function(err, documents) {
res.send(documents.map(function(d) {
return { title: d.title, id: d._id };
}));
});
});
On this example, the function loaduser() is used for authentication purposes:
function loadUser(req, res, next) {
if (req.session.user_id) {
User.findById(req.session.user_id, function(err, user) {
if (user) {
req.currentUser = user;
next();
} else {
res.redirect('/sessions/new');
}
});
}
}
What I don't understand is:
I suppose that node.js, before start executing the app.get, it goes to loaduser function. loadUser() function has three parameters: req,res,next, but I don't see, at least, how you pass from app.get() the "req" parameter to loadUser(). From where does it come?
Inside loadUser() function, when you execute next(), it means that the function app.get()" can continue its procedure, but this req.currentUser = user, is the same req that is used on app.get() function?
Inside loadUser() function, when you execute res.redirect() code, automatically breaks the procedure on app.get() function, right? it looks like it doesn't return to Document.find().
The questions you've asked are about the Express framework internals specifically:
When you call app.get(route, loadUser, final) Express will make a stack (array) with the loadUser and final function functions and it will know that when you call next it should execute the following function in the stack with the same req and res params.
When you call next it will just pass to the next function in the middleware stack.
Since you call res.redirect and you don't call return, it won't pass to the next function in the stack (the one with Document.find).
Resources:
http://howtonode.org/getting-started-with-express
I think in order to be comfortable with this you need get familiar with idea of middleware and how it's used in connect framework.
I've found few articles where this subject explained well enough. Take a look there:
http://howtonode.org/connect-it
and here http://stephensugden.com/middleware_guide/
the main idea is you have a set of layers and each time when new request arrives it goes through each level and on each level you can decide what to do with that. You can stop at some level, do something and return response or you can pass it to the next layer

Resources