Consolidating Routes in Express.js - node.js

I'm a novice programmer working on a web app. As I have things right now there is a route for every single query to my database. I know there must be a way to use route parameters to direct the route to executing the right function but I am having problems in implementation.
Here is what my routes look like right now:
var database = require('./routes/database');
app.get('/query/type', database.type);
app.get('/query/test', database.test);
app.get('/query/another', database.another);
app.get('/query/onemore', database.onemore);
Each route is mapped to a function in the database.js file. I would like to try to implement something in the following format which would handle the queries with a single line:
app.get('/query/:query', database.query)
Where it executes whichever function is named in the parameter :query.
Is there an easy way of implementing this?

you can create a function that will parse the parameter and use associative array to build the function you want to execute then invoke it. see code below.
function parseParam(req, res) {
var func = database[req.param('query')];
func(req, res);
}
app.get('/query/:query', parseParam);

Related

Expressjs higher order router vs appending to request

Let's say I want to pass to an ExpressJS route callback an object.
I know I can append to app:
// router.js
const getFoo = (req, res) => res.json(req.app.foo);
// index.js
const app = express();
app.foo = {};
app.get('/foo', getFoo);
or I can use a higher order function:
// router.js
const getFoo = foo => (req, res) => res.json(foo);
// index.js
const app = express();
const foo = {};
app.get('/foo', getFoo(foo));
Both are easy to write, extend and test.
But, I don't know the implications of the solutions and whether one is better.
Is there anyone knowing real differences between the two approaches?
I think the second solution is more correct, here's why.
imagine you get used to the first solution and one day you need to send something called post or get or anything with the name of app property and you forget that there is already a property named like that, so you override original property without even realizing and when you call app.post() program will crash.
Believe me, you don't want hours of research wasted on something like that and realizing that you simply overrode original method
Also, in my opinion, it's always a bad idea mutating original object which wasn't generated by you
As #vahe-yavrumian mentioned it is not a good idea to mutate the state of the object created by a third party library.
between you can also use app.get() and app.set() methods to pass any data to the other routers in the queue (seems those methods are there just for this purpose.)
more information at https://expressjs.com/en/api.html.
The second solution easily allows you to pass different value for foo on different routes, if you ever found a need to do that.
The first solution essentially puts the value on the app singleton, which has all the implications of using singletons. (And as mentioned by #Anees, for express specifically the app settings with get and set are the proper place to store this, not a custom property)

NodeJS, express - routing

I´ve setup a little NodeJS API with 'expressjs'. In my code I have some similar code blocks looking like this:
app.get('/api/my/path', (req, res) => {
doSomethingUseful();
});
All thoses requests do something different, so the function being called is always different too.
I´m asking myself if this is good practice for writing code in NodeJS or not.
If there is a better / cleaner way of writing all those paths I would appreciate if you could tell me how or at least giving me a place to look for it.
EDIT 1: To be clear: I ask if it´s a good idea to write a lot of 'app.get(...)' into one source code file or if there is a better way?
Yes there is a better way than writing all routes in one file. For example, let us say you have routes for users and questions.
For users, you want get/set/put/delete for profile, and similarly for questions.So you create the following folder structure: /api/users and /api/questions
In /api/users,
const express=require('express')
const router=express.Router()
//this handles route: /users
router.get('/',(req,res)=>{})
//this handles route: /users/profile
router.get('/profile',(req,res){})
//this is to retrieve profile of specific user by user id :/users/profile/:userID
router.get('/profile/:userId',(req,res))
router.post('/profile',(req,res))
.
.
Then, in your index.js or entry point of your project,
const users=require('./api/users')
const questions=require('./api/questions')
app=require('express')
app.use('/users',users)
app.use('/questions',questions)
So in effect, you say for any /users route, refer to the users.js file, for any /questions routes, refer questions.js file and so on
Use a simple module, don't be invoking routes until you form a heap.
Try Route Magic
You want to do just 2 lines of code and have the module read your directory and file structure to handle all the app.use routing invocations, like this:
const magic = require('express-routemagic')
magic.use(app, __dirname, '[your route directory]')
For those you want to handle manually, just don't use pass the directory to Magic.

node - diagnostics with repl.start() - access to all variables etc

I'm running a server with node.js (espress). I've defined a diagnostic/debugging function which I can add to any controller. The function mostly just prints out values of certain variables in the console.
Example:
function ConsoleDebug(req, res) {
console.log(variable1)
console.log(req.someObject.otherObject.variable2)
}
and is added to a controller like so:
router.get('/URL', function(req, res, next) {
ConsoleDebug(req, res);
res.render('someview');
});
I want to make the whole diagnostic/exploratory process more dynamic and I've read a whole deal of great things about the Node.js REPL and I figured I wanted to run it at any point in code.
I've added it to the ConsoleDebug() function, so it runs anytime the router is triggered using
function ConsoleDebug(req, res) {
console.log(variable1)
console.log(req.someObject.otherObject.variable2)
repl.start({useGlobal: true})
}
It starts nicely, but it doesn't have access to any of the variables or scope at the point it is started. I would like to be able to write
req.someObject.otherObject.variable2
and get the same output as is produced by
console.log(req.someObject.otherObject.variable2)
Ultimately, I'd like to be able to use the REPL to dynamically tinker with any other node.js command at any point in code with respect to the scope where it was started, so that I don't have to keep rewriting the code in ConsoleDebug(), saving, restarting the server and observing the results.
Is that even doable or is there a more appropriate method/tool?

Passing parameters to express middleware in routes

I am building a nodejs + express RESTful server, and am trying to leverage middleware to ease authorization of specific actions.
What I am trying to achieve is to pass parameters to my authorization middleware functions. I was wondering if it is at all possible to do this in the routes or if I have to extract the parameters in the middleware function. I was hoping to avoid that behavior as I have been *hum* not entirely consistent in my URL parameter names.
What I would like to do is something like this:
router.get(
'/:productId',
auth.can_get_this_product(productId), // Pass the productId here
controller.perform_action_that_requires_authorization
);
But this is not possible. Because I have other routes where the names of parameters might not be the same (ie: router.get(/:p_id, auth.can_get_thi...). I realize I should probably just go back and make sure that my parameter names are consistent everywhere and retrieve the parameters in the middleware using req.param('productId')but I am curious if it would be at all possible.
Well, I suppose you can pass the params hash key and then use that.
router.get(
'/:productId',
auth.can_get_this_product('productId'), // Pass the productId *KEY* here
controller.perform_action_that_requires_authorization
);
//....
function can_get_this_product(productIdKey) {
var productId = req.params[productIdKey];
//....
}
But of course, we both know you should just bite the bullet and refactor those names.

Overloading functions for an api in node - Best practice?

I'm currently building a small express powered node app to power a RESTful API exposing data from a node module I wrote. One of the functions in the module takes three arguments, but I want to allow the usage of the API by specifying just one, two, the other two or all three arguments.
So even starting to write the routes like this already feels ridiculous.
app.get('/api/monitor/:stop/:numresults', apiController.monitorNum);
app.get('/api/monitor/:stop/:timeoffset', apiController.monitorOff);
app.get('/api/monitor/:stop', apiController.monitor);
Especially since I don't know how to specify the difference between the first two, as numresults and timeoffset are both just integers.
What would a best practice in this situation look like?
The first problem you face is that you have identical routes, which are not possible if you're using express (I'm assuming that's what you're using). Instead you probably want one route and utilise the query object instead:
app.get('/api/monitor/:stop/', function (req, res, next) {
var stop = req.params.stop,
numResults = req.query.numResults,
timeOffset = req.query.timeOffset;
yourFunc(stop, numResults, timeOffset);
});
That way you can call the api with the following url: http://example.com/api/monitor/somethingAboutStop/?numResults=1&timeOffset=2. It looks like the stop parameter can also be moved to the query object but it's up to you.
You can use a catchall route then parse it yourself.
Example:
app.get('/api/monitor/*', apiController.monitor);
Then in apiController.monitor you can parse the url further:
exports.monitor = function(req, res) {
var parts = req.url.split('/');
console.log(parts); // [ '', 'api', 'monitor', '32', 'time' ]
console.log(parts.length); // 5
res.end();
};
So, hit the /api/monitor/32/time, and you get that array above. Hit it with /api/monitor/something/very/long/which/you/can/parse and you can see where each of your params go.
Or you can help yourself, like /api/monitor/page/32/offset/24/maxresults/14/limit/11/filter/by-user
Though, as Deif has told you already, you usually do pagination with query parameters, maxResults & page being your usual params.

Resources