node.js - mean.io implicit/hidden parameters - node.js

i'm fairly new to node.js so this could potentially a total noob question. Anyway. I discovered the mean.io Project. In the official article-example on Github, there is the following method in the article-controller.
exports.update = function(req, res) {
var article = req.article;
article = _.extend(article, req.body);
article.save(function(err) {
if (err) {
return res.jsonp(500, {
error: 'Cannot update the article'
});
}
res.jsonp(article);
});
};
With a corresponding route
module.exports = function(Articles, app, auth) {
app.route('/articles')
.get(articles.all)
.post(auth.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.show)
.put(auth.requiresLogin, hasAuthorization, articles.update)
.delete(auth.requiresLogin, hasAuthorization, articles.destroy);
// Finish with setting up the articleId param
app.param('articleId', articles.article);
};
So I'm confused. When and where does the route pass the req/res parameters to the articles.update, or any other articles function? Is there some hidden mechanism in node/express/mean I've missed out?
Thanks in advance.

app.route('/articles/:articleId')
.get(articles.show);
This means express will invoke articles.show method with request and response as first two parameters when a GET request comes with matching path
.

Related

How to set Routes for APIs

I am building an API to manage meetups with nodeJS. I have build an endpoint with the route "/meetups/:id/" to fetch a specific meetup record by its id. And then I want to fetch all the upcoming meetup records and I tried to use "/meetups/upcoming/" but when I query it, I get the not found error (404). It seems like the second route is not recognised.
Here is the code defining the two routes
the request from postman
Any help on how can I handle that?
Thanks.
Route is '/api/v1/meetups/upcoming/all'. Move res.status outside the map function.
EDIT: you'll have to change the route which has to be different from api/v1/meetups/:id. Reason is when route '/api/v1/meetups/upcoming' is requested express sees it as the same route as before and takes 'upcoming' as the parameter.
app.get("/api/v1/meetups/upcoming/all", function(req, res) {
var today = new Date();
var upcomings = db.meetups.map(function(meetup) {
if(meetup.happeningOn > today) {
return meetup;
}
});
res.status(200).send({
status: 200,
data: upcomings
});
});
You need to move the res.status piece outside of the const upcomings definition.

How do I make sure a promise has been returned before responding to an incoming request (Swagger/Express)

I'm trying to write a simple Swagger API that will allow me to sync a couple of systems on demand. The syncing is one way, so basically the end goal will be to send a request to both system, see what's new/changed/removed on the origin, then update the destination. I've been trying to do this using node.js instead of Java, to which I'm more used to, as a learning experience, but I'm really having a hard time figuring out a key issue due to the async nature.
As a test, I constructed a simple Express node.js app on IntelliJ, where in one of the routes I'm calling a function exported from a different file and trying to get the response back. Unfortunately, this isn't working so well. What I've done is this:
getit.js - (this goes to the Ron Swanson generator to get a quote)
const rp = require('request-promise');
async function dorequest() {
const response = await rp(uri);
return Promise.resolve(response);
};
module.exports = {dorequest}
In the route I've done this:
var getit = require ('./getit.js');
/* GET users listing. */
router.get('/', function(req, res, next) {
var ret = getit.dorequest();
res.send(ret);
console.log('res out' + ret);
});
What I get in the console is
res out[object Promise]
and the response is of course empty.
What am I doing wrong? I've been playing with this for a week now, tried various methods, but I keep getting similar results. I'm obviously missing something out, and would appreciate some help.
Thanks!
Object is empty because it was written on the console before the Promise is resolved. You have to wait until Promise is resolved and then send the response back so try to change your code like this:
var getit = require ('./getit.js');
/* GET users listing. */
router.get('/', function(req, res, next) {
getit.dorequest().then(function(data) {
console.log('res out' + data);
res.send(data);
});
});
Since you are using async/await approach all you need to do is to place await before getit.dorequest();
so this line will look like var ret = await getit.dorequest();

How to use params to filter database objects with Sailsjs

I'm still a bit new to Node in general, so I'm sorry if this is noob question. My setup is Sailsjs + MongoDB.
I have a RESTful API controller set up to handle the "lab" collection in my DB.
Here is what I use in my controller to pull up all the objects in this collection when /lab/ is used:
index: function (req, res, next) {
Lab.find(function foundLabs(err, labs) {
if (err) return next(err);
res.view({
labs: labs
});
});
},
In this collection there are fields for "site" and "lab" and I'd like to be able to filter what shows up with params like:
/lab/:site
/lab/:site/:lab
So if "/lab/aq" was pulled up it would get all objects in the AQ site and if "/lab/aq/123" was pulled up it would get the objects for the 123 lab in the AQ site.
I know this would likely be done with the Lab.find function, but I haven't been able to find any documentation which gives me an answer I'm looking for.
Any help would be appreciated.
In your config/routes.js file you need to add a route with optional parameters:
'/findLabs/:site?/:lab?': 'LabController.findLabs'
// use a different route than 'lab' to avoid interfering with the blueprint routes
Then, in your LabController.js, if the requested url had site and/or lab, you will find them in req.params:
// when a request is sent to '/findLabs/aq/123':
findLabs: function(req, res, next) {
sails.log(req.params) // {site: 'aq', lab: '123'}
// you can use them to filter:
var query = Lab.find();
if (req.params.site) query.where({site: req.params.site});
if (req.params.lab) query.where({lab: req.params.lab});
query.exec(function(err, labs) {
if (err) return res.serverError();
res.json(labs);
});
}

how to publish a page using node.js

I have just begun to learn node.js. Over the last two days, I've been working on a project that accepts userinput and publishes a ICS file. I have all of that working. Now consider when I have to show this data. I get a router.get to see if I am at the /cal page and..
router.get('/cal', function(req, res, next)
{
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
//console.log(ical.toString());
});
});
res.send(ical.toString());
// res.render('index', {
// title: 'Cal View'
// })
})
So when /cal is requested, it loops through my db and creates an ICS calendar ical. If I do console.log(ical.toString) within the loop, it gives me a properly formatted calendar following the protocol.
However, I'd like to END the response with this. At the end I do a res.send just to see what gets published on the page. This is what gets published
BEGIN:VCALENDAR VERSION:2.0
PRODID:calendar//EN
END:VCALENDAR
Now the reason is pretty obvious. Its the nature of node.js. The response gets sent to the browser before the callback function finishes adding each individual VEVENT to the calendar object.
I have two related questions:
1) Whats the proper way to "wait" till the callback is done.
2) How
do I use res to send out a .ics dynamic link with
ical.toString() as the content. Do I need to create a new view for
this ?
edit: I guess for number 2 I'd have to set the HTTP headers like so
//set correct content-type-header
header('Content-type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
but how do I do this when using views.
Simply send the response, once you got the neccessary data! You are not required to end or send directly in your route but can do it in a nested callback as well:
router.get('/cal', function(req, res, next) {
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
});
res.type('ics');
res.send(ical.toString());
});
});
I also included sending the proper Content-Type by using res.type.
Also: Don't forget to add proper error handling. You can for example use res.sendStatus(500) if an error occured while retrieving the documents.

Angular UI Client Side Pagination

I would like to enable pagination and I'm torn between client side and server side pagination. In the long term (more data) it is probably better to do server side pagination, but I haven't found a good tutorial on it.
I use Angular/Express/Mongo. I have the Boostrap UI in use, and would like to use their pagination directive for pagination. I have read some articels on how to kind of do it, but they are outdated and I cannot get it to work. http://fdietz.github.io/recipes-with-angular-js/common-user-interface-patterns/paginating-through-client-side-data.html
Could anybody help me get that example to work with Bootstrap UI for Angular?
If you have a set number of items per page, you could do it this way :
Define an angular service to query the data on your server.
.factory('YourPaginationService', ['$resource',
function($resource) {
return $resource('baseUrl/page/:pageNo', {
pageNo: '#pageNo'
});
}
]);
Call it via the angular controller. Don't forget to inject your service, either globally or in the controller.
$scope.paginationController = function($scope, YourPaginationService) {
$scope.currentPage = 1;
$scope.setPage = function (pageNo) {
$scope.currentPage = pageNo;
YourPaginationService.query({
pageNo: '$scope.currentPage'
});
};
};
On express 4 (if you have it), set up your route.
app.route('/articles/page/:pageNo')
.get(data.listWithPagination) //random function name
Then you need to wire that function with the desired Mongo request in your Node controller. If you have Mongoose, it works like this :
exports.listWithPagination = function(req, res) {
var pageLimit = x; //Your hardcoded page limit
var skipValue = req.params.pageNo*pageLimit;
YourModel.find() //Your Mongoose model here, if you use Mongoose.
.skip(skipValue)
.limit(pageLimit)
.exec(function(err, data) {
if (err) {
return res.send(400, {
message: getErrorMessage(err)
});
} else {
res.jsonp(data);
}
});
};
That's how I would do it on a typical MEAN stack. If you're working with different libraries/technologies, you might need to adapt a few things.

Resources