i got a series of routes like :
var router = express().Router;
router.get('/',middleware1,middleware2);
router.get('/courses',middleware1,middleware2,..)
router.post('/dates',middleware1,middleware2,..)
app.use('/u',router);
Now, when i declare the root of the route in
app.use('/u',router);
i would like to put a mandatory parameter that has to be present in every route of the micro-app like
app.use('/u/:user_name/',router);
So that i can check for every route with router.param the existence of this param in a db.
Is something like that possible by defining the param in the root of the micro app and not in every single route like
router.get('/:user_name/',middleware1,middleware2);
router.get('/:user_name/courses',middleware1,middleware2,..)
router.post('/:user_name/dates',middleware1,middleware2,..)
as they are actually a lot in the real application and it would be a pain to change them all?
Really thanks !
You should be able to do this using mergeParams so long as you're using a recent enough version of Express.
var router = express.Router({mergeParams: true});
Then this will work:
app.use('/u/:user_name/', router);
mergeParams is documented here:
https://expressjs.com/en/4x/api.html#express.router
Related
I created a router file using Express. The callback functions reside in their discrete "controllers" files. Following is an excerpt of the parts relevant to my question, and lines such as require of controller functions have been omitted:
const express = require('express');
const router = express.Router();
// This should run first
router.param('coolParamName', validateParamBeforeHandlingReqs);
// Param name is ↑↑↑↑↑ "consumed" here, although NOT defined yet
// This should run after the above code
router.route('/').get(getAllUserNames).post(createUser);
router.route('/:coolParamName').get(getUserName).patch(updateUser).delete(deleteUser);
// Param name is ↑↑↑↑↑ defined here, and was consumed earlier - how?
As the comments explain, it seems like the param name has been defined as coolParamName on the bottom, but "consumed" by the code written above it. This feels strange to me, because I feel it's natural to define first and then use later - is it just me? Did I write a code that's against the intended design pattern?
I would like to understand how Express defines the name of param, and how router.param and router.router handle them.
router.param('coolParamName') essentially registers a callback that will get called for any route (in that router) that uses the :coolParamName parameter and matches the current request. The callback will get called once per request BEFORE the route that matches the request that contains the :coolParamName parameter.
It's kind of like middleware for a matching parameter. It allows you to automatically configure some setup code anytime that particular parameter is matched in a route.
FYI, I expect that router.param() may be one of the least used features of Express since it can be accomplished many other ways, but it probably works best for validation-type code that checks named properties for validity before the route itself gets called.
You could accomplish the same thing by just using a piece of middleware on that specific route too or even just calling a function inside the route handler. So, this is just a nicety feature if you happen to use the same parameter in multiple routes.
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)
const route = Router();
router.get('user/:UserId');
router.post('user/:UserId');
router.put('user/:UserId');
I have the above routes for user REST api's, i wanted to have a user by location api. so i tried something like below.
router.get('user/getUserByLocation');
The problem is it calls the router.get('user/:UserId'); as it finds the getUserByLocation as UserId.
I changed the router.get('user/getUserByLocation'); to router.get('getUserByLocation'); it works.
My question is , is the above solution the best approach or i need to use the router.get('user'); with querystring to get the getUserByLocation.
You can declare in router that :UserId should be a number (and not a string) using (\\d+). This way user/getUserByLocation shouldnt match your route user/:UserId:
const route = Router();
router.get('user/:UserId(\\d+)');
router.post('user/:UserId(\\d+)');
router.put('user/:UserId(\\d+)');
router.get('user/getUserByLocation');
You have to configre getUserByLocation before router.get('user/:UserId'). Change the order of your code. Configure router.get('user/getUserByLocation') first.
In express, the first matching request handler is executed, so if router.get('user/getUserByLocation') is configured after router.get('user/:UserId'). That will get called before that.
const route = Router();
router.get('user/getUserByLocation');
router.get('user/:UserId');
router.post('user/:UserId');
router.put('user/:UserId');
I am building simple app to teach myself node and I've got this problem.
Console.log is not executed at all. Do you have any idea why?
I have 404 error but nothing else. App doesnt crash, I just cant get this view.
router.get('/charts/top/:exercise', function(req, res, next) {
console.log("exercise: " + req.params.exercise);
res.render("top", {});
});
SOLUTION: Since this was in separate routes folder in charts.js file, there should not be /charts but just /top/:exercise
Assuming your router comes from :
var express = require('express');
var router = express.Router();
You will need to export this module for your server to know that these routes are handled. with the following :
module.exports = router;
Under all your routes definitions.
Now, your app just needs to call this said file with the routes to be handled.
var handler = require('./handler');
-Some code-
app.use('/', handler);
.use will set the primary pivot of the route to your router. It gives the ball to it. In this exemple, everything from / will be passed to router. If you really want to make this clean, change your router variable in your file to charts and use it in your app to everything that starts with /charts
Then in your charts file you will be able to handle /top/:exercice
You can make multiple levels of routing like this with .use to set your new point of handling.
Also, I would like to point out that you don't need "next" in your function because you do not have any callback.
Edit : And remember, if 2 routes could be handled, it will always be the first one declared to catch it. That way, declare some routes with a if else logic behind it. Giving the more specific choice first.
I'm new to node, blah blah
I'm looking through some code I found, and encountered the lines
var app = express();
var glob = require('glob');
var controllers = glob.sync(config.root + '/app/controllers/*.js');
controllers.forEach(function (controller) {
require(controller)(app);
});
I understand that this goes and gets all the filenames of every file in /app/controllers/ that ends with .js, and sticks them in an array, then iterates over each one and calls a require on it. Makes sense, and looks like a nice way of not requiring each one individually. My question is what's that last (app) for? I tried looking at node documentation, but there is no require()() function. editing out the (app) leaves the code working fine with no errors, but doesn't load the controllers. If I had to take a guess, is it 'multiplying' the app by the found controller? Why would app.require(controller) not be a suitable option?
Thanks in advance
require is a part of node and is how modules are loaded. When you edit out the (app), it is probably still loading the controllers but because you haven't passed the app object over to each controller, they fail silently or return a controller object that doesn't work.
So something in the controller (probably a route being created or other similar stuff) needs to know about the app object and it has to be passed into the controller because app isn't in the global scope. You may want to review the node docs for module and globals as those will probably clear up WAY more than just this one question.
In my estimation we will have:
/* some-controller-file.js */
module.exports = function (app) {
/* do things with `app` */
}
So this allows you to use the created app inside of the controllers probably so you can attach routes.