0

REST API and Node newbie here. I'm trying to make a REST API in nodejs. Each user would be able to get items associated with their userid and the authentication mechanism is obviously separate from the url. I'm working with something like the following services.

/api/users/userid
/api/users/userid/cars/carid

I expect to have other APIs similar to the 2nd one in the future.

The issue is how to 'bind' the userid and carid or houseid parameters in a clean way. My hope was to be able to have an intermediate step which would capture the 'userid' parameter in the users.js file and then it would delegate the request to the other components. I hoped that this would allow me a more concise authentication checking but there may be more preferable ways. The problem right now is that userid is guaranteed to be unique but carid and houseid are only unique per user so I will need both values to retrieve the data.

My Question is how can I achieve this or is there a better way to organize this to facilitate concise reusable code in terms of authenticating that a user has access to that API. Also if this is very unREST-like, please correct me.

And I have the following code:

app.js

var express = require('express');
var app = express();
app.use('/api', require('./routes/api'));
var server = app.listen(3000, function() {
    var host = server.address().address;
    var port = server.address().port;

    console.log('The server is up at ' + host + ':' + port);
});

./routes/api.js

var express = require('express');
var router = express.Router();

/* GET api listing. */
router.get('/', function(req, res, next) {
    res.send('Base of our APIs');
});

router.use('/users', require('./users.js'));

router.use('/', function(req, res) {
    res.send('Unauthorized api access. Not authorized for ' + req.baseUrl);
})



module.exports = router;

./routes/users.js

var express = require('express');
var router = express.Router();

router.use('/:user_id/cars', function(req, res, next) {
    console.log('Userid is:' + req.params.user_id);
    // Authenticate a user here before sending to the next page
    var cars_api = require('./cars.js');
    cars_api(req, res, next);
});

module.exports = router;

./routes/cars.js

var express = require('express');
var router = express.Router();

/* GET cars for user */
router.get('/', function(req, res, next) {
  res.send('Retrieving all cars for user ' + req.params.user_id);
});

router.get('/:car_id', function(req, res, next) {
    res.send('Retrieving car with car_id ' + req.params.car_id + ' and user id ' + req.params.user_id);
});

module.exports = router;

I've tried several other ways of calling the cars API that all req.params values are cleared in between. I'm using WebStorm to debug.

Eagle
  • 153
  • 4
  • 1
    Why can't you use `/:userid/cars/:carid`? – Scimonster Feb 08 '15 at 15:35
  • Okay. So what you want to do is have separate controllers for users and cars. Instead of having 1 big route, you want the request to go through each controller and then a response returned. Here, it would go through users and cars controller and the cars controller would return the response. Is that what you want to do ? – Noman Ur Rehman Feb 08 '15 at 16:15
  • @Scimonster where are you suggesting I use that? – Eagle Feb 08 '15 at 17:42
  • In the API root or something. – Scimonster Feb 08 '15 at 17:43
  • @NomanUrRehman That was the idea. I had concerns about how large my route list might grow in the future and wanted to try to consolidate the code for checking access authorization. This approach is not mandated; I'm just trying to find a good approach. – Eagle Feb 08 '15 at 17:47
  • @Scimonster No reason I can't. I was hoping to encapsulate all 'authorization' type logic into the user controller (or there about) so the cars controller wouldn't have to test for it (as access is currently on the user by user level). In thinking about it more, it may make sense to defer util later. Still trying to understand the best approach. – Eagle Feb 08 '15 at 17:52
  • 1
    @Eagle I am not sure it is possible to do it the way you have described where you can mount multiple controllers to a single route but as a starting point, for authentication/authorization or for code that has to run before every request, we can use request middleware. Middleware is code that can be executed before a request without interfering with the normal request flow. You can also use middleware to stop a request from propagating further if certain conditions are not met like a user session is not set for example. – Noman Ur Rehman Feb 08 '15 at 17:55
  • @NomanUrRehman Thanks for the middleware advice. I'll look into that. I'll probably just abandon this train of thought as Scimonster also seems to be indicating the same sort of thing. Thanks for the info! – Eagle Feb 08 '15 at 18:09

2 Answers2

1

You would not necessarily need to write the full route like that, something you might want to look into is nested routes, I think that would help.

Rest with Express.js nested router

here you have a great example I started using when I built my first api.

Community
  • 1
  • 1
Santiago Esquivel
  • 380
  • 1
  • 5
  • 15
  • Right on. This is more or less what I was looking for. – Eagle Mar 20 '15 at 05:48
  • With nested routes there are a lot of variations you can do as in nest certain routes while leaving others independent. As an example: you can have /api/login then have /api/users/names (this will have names nested into users) but then create another route like /api/population/area (where area is nested to population) but independent from /api/users/names, therefore req.params.names would only work on the route which it is part off and vice versa, hopefully I explained myself clearly – Santiago Esquivel Mar 23 '15 at 14:15
0

At the suggestion of commenters, I have decided to just write routes directly to the cars api in the form of /users/:user_id/cars/:car_id.

Eagle
  • 153
  • 4