Node.js / Express req.params cleared when delegating request - node.js

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.

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.

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.

Related

How to send a variable from one router to another

Please, I wand to send a variable from login.js to dashbaord.js using node.js.
This is my dashboard.js:
var express = require('express');
var router = express.Router();
var Firebase = require('firebase');
var login = require('./login');
var user = login.userId;
var admin = require("firebase-admin");
var login = require('./login');
/* GET home page. */
router.get('/', function(req, res, next) {
console.log(user) // this return an undefine
res.render("pages/dashboard");
});
module.exports = router;
Thanks in advance.
The code in routes only runs when the URL pattern is hit. So in the code below the value in user will be printed out every time the "/" gets hit but you only initialized user once.
router.get('/', function(req, res, next) {
console.log(user) // this return an undefine
res.render("pages/dashboard");
});
The line for var user = login.userId; is only set once when your main express app is booted up. That is why it's coming back as undefined. But you shouldn't be passing user information from module to module this way, thats what sessions are for.
Sessions & UserIds
If you want to get the userId you should be storing it in the users session or in a cookie then you can pull it out of the request object. Any data that is specific to a user you can retrieve from your database using their userId in the session. With express theres no built in support for sessions but you can npm install middleware that will take care of this for you so that you can then write.
router.get('/', function(req, res, next) {
console.log(req.session.userId)
res.render("pages/dashboard");
});
Environment Variables
If you need to pass configuration settings around across multiple modules you can use
process.env.${propertyName} for example process.env.cat = "Henry". But that is for things you set once that are the same for all users.

express.js API Endpoint Best Practice for CRUD

I'm building a REST API using express.js.
My API needs to provide all endpoints to do CRUD on "users", so I have the following so far:
app.get('/users', getAllUsers); // get all users
app.post('/users', createUser); // create a new user
app.put('/users', updateUser); // update a user
app.delete('/users', deleteUser); // delete a user
But whats a good practice endpoint to get a single user?
So ideally if you GET on /users/1234 I should only return User with ID 1234 but if I just do /users I should return all users as the ID was not detected.
How can I refactor my:
app.get('/users', getAllUsers);
to handle both cases?
You can create a higher-level router, and then call app.use() / router.use() on it. This is the recommended approach in the Express documentation.
var express = require('express');
var app = express();
var usersRouter = express.Router();
usersRouter.get('/', function(req, res) {
res.send('Got all users');
});
usersRouter.get('/:id', function(req, res) {
res.send('Got user ' + req.params.id);
});
app.use('/users', usersRouter);
You cant , the best practice is to isolate your route operations as much as possible. There to get a single id,such route must only get that data.
Example.
var router = express.Router();
router.route('some/route/id/123').get(function(req,res){
console.log('id');
});
router.route('some/route/all').get(function(req,res){
console.log('all');
});
you can do so by providing id as an optional param and checking that in the controller function
for eg.
app.get('/users/:id?', getAllUsers);
But I'd prefer to go with single responsibility principle
app.get('/users', getAllUsers);
app.get('/users/:id', getUser);

How can I still use a router in node express and still have a wildcard and params?

So basically I want my code being able to go mywebsite.com/username and itll take them to their profile.
My app.js has -
var user = require('./routes/user');
app.use('/*', user);
and then in my user.js I have
router.get('/:username', function(req, res) {
console.log('the user name', req.params.username);
})
If I change router.get to just router.get('/') my console.log logs out undefined and if I leave it as router.get('/:username') I get a 404.. I also tried doing app.use('/:username', user) as well but that still didn't work.
You should't need to have a wildcard like you do on app.use
Try something like this.
Also see the express router documentation http://expressjs.com/en/guide/routing.html
// Required Moduiles
var express = require('express');
var router = express.Router();
var app = express();
// Add route to get
router.get('/:username', function(req, res) {
res.status(200).send(req.params.username)
})
// Add username
app.use('/', router)
// Start express server
app.listen(3030);
Update -
Just using app.use(router) would do the same thing if all your routes are going to be contained in this new router instance / middleware you are creating in a separate file.

Express.js - How can I pass a url param from the main router to another router module?

In my small chat application, when I make a HTTP call to an endpoint ending in "users" I want my room router to take over. This be with a preceding room name, or with nothing at all.
routes.js:
module.exports = function(app) {
var roomRoute = require('./routes/room');
app.use('/:roomname/users', roomRoute);
app.use('/users', roomRoute);
};
Now when my room router takes over, I want to be able to access the room name if it exists in the url. Something like:
./routes/room.js:
var express = require('express');
var router = express.Router();
router.get('/info', function(req, res) {
console.log('the roomname is ' + req.roomname);
res.end();
});
module.exports = router;
Is there any way to easily pass this room name along? I could probably extract it from the baseUrl but I think there is probably a better way...
The router needs to set the mergeParams option to true.
http://expressjs.com/en/4x/api.html#express.router
let router = express.Router({ mergeParams: true });
The very next thing I read after I posted had the answer...
In routes.js:
var roomRoute = require('./routes/room');
app.use('/:roomname/users', function (req, res, next) {
req.roomname = req.params.roomname;
next();
}, roomRoute);
app.use('/users', roomRoute);

Creating a Modular REST API in ExpressJS

I'm having some trouble creating a RESTful API in Node/Express. In the app I'm building, a user has many messages, and messages belong to users. I need to be able to make an HTTP requests to retrieve all messages by a particular user. Here's the basic structure of the app, starting with the basic server, which delegates routing to a file called 'config/middleware.js'.
//server.js
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
require('./config/middleware.js')(app, express);
var port = process.env.PORT || 8080;
app.use(bodyParser.json());
app.use(express.static(__dirname + '/../client'));
app.listen(port);
This is the middleware file where we send requests to the appropriate router. A request made to 'users/5/messages' would get routed to the messages router, and a request made to 'users/5' would get routed to the users router.
// config/middleware.js
module.exports = function(app, express) {
var usersRouter = express.Router();
var messagesRouter = express.Router();
app.use('/users/:userId/messages', messagesRouter);
app.use('/users', usersRouter);
require('../routers/users')(usersRouter);
require('../routers/messages')(messagesRouter);
};
This is the messages router. If a get request is made to '/users/5/messages', I want the getAllMessages function to be run, which should return all messages by the user with userId 5.
// routers/messages.js
var messagesController = require('../controllers/messages');
module.exports = function(app) {
app.get('/:messageId', messagesController.getMessage);
app.get('/', messagesController.getAllMessages);
};
The problem is that the getAllMessages function doesn't have access to the 'userId' parameter (with value of 5), which is required in order to make an appropriate query to the database. The getAllMessages function in the controller expects the userId to be stored on req.params.userId. Is there any way to get the userId of 5 to be present on the req.params object inside the getAllMessages function?
The req.params are not passed down the route chain. To do so, you could do something like
In server.js, create a key on req. This will pass on your data between routes. Do this before mounting the routes.
app.use(function (req, res, next) {
req._data = {};
next();
});
In config/middleware.js,
module.exports = function(app, express) {
var usersRouter = express.Router();
var messagesRouter = express.Router();
// attach usedId
app.use('/users/:userId/messages', function (req, res, next) {
req._data.userId = req.params.userId;
next();
});
// mount the router
app.use('/users/:userId/messages', messagesRouter);
app.use('/users', usersRouter);
require('../routers/users')(usersRouter);
require('../routers/messages')(messagesRouter);
};
This way, you would have access to req._data.userId in routers/messages.js.
Side note: A better way to structure the routes would be to use something like, (read shameless plug), https://github.com/swarajgiri/express-bootstrap/blob/master/web/routes.js
You can use app.locals or res.locals to pass some datas.
There is a good explanation about locals.
An usage sample:
app.locals.userid = req.params.userId; //binding userid
app.locals.userid // => '5'
OR: put a global variable.
user_id_tmp = req.params.userId;
Now this is become global variable in app. So you can call user_id_tmp variable from anywhere.
I was looking for same.
Here is modules app example on github and auther site
Also we can change or update structure base on our requirements

Resources