Express routes parameter conditions - node.js

I have a route on my Express app that looks like this:
app.get('/:id', function (request, response) {
…
});
The ID will always be a number. However, at the moment this route is matching other things, such as /login.
I think I want two things from this:
to only use this route if the ID is a number, and
only if there isn't a route for that specific paramater already defined (such as the clash with /login).
Can this be done?

Expanding on Marius's answer, you can provide the regex AND the parameter name:
app.get('/:id(\\d+)/', function (req, res){
// req.params.id is now defined here for you
});

Yes, check out http://expressjs.com/guide/routing.html and https://www.npmjs.com/package/path-to-regexp (which express uses). An untested version that may work is:
app.get(/^(\d+)$/, function (request, response) {
var id = request.params[0];
...
});

You can use:
// /12345
app.get(/\/([^\/]+)\/?/, function(req, res){
var id = req.params[0];
// do something
});
or this:
// /post/12345
app.get(/\/post\/([^\/]+)\/?/, function(req, res){
var id = req.params[0];
// do something
});

Related

first portion of route url is not included

I have a route where I built two GET APIs. I would like one to redirect from /download to /zip all while passing a parameter. The problem is I am getting a 404 for some reason the routes url is not being included in the redirect()
Here are the APIs.
// respond with xml from from static folder
router.get('/zip/:id', function (req, res) {
fileName = req.params.id
});
router.get('/download', function (req, res, next) {
var id = req.query.id
res.redirect('/zip?id='+ id);
});
module.exports = router;
I get a 404 when testing the URL:
localhost:8000/rest/pluto/v1/plugin/download?id=networktool
I am thinking it might be how I have the middleware setup but not real sure. I'm still new to node/express.
You are redirecting to a route that isn't actually defined. With your /zip/:id route definition:
router.get('/zip/:id', function (req, res) {
var fileName = req.params.id
});
The way that is defined, you have to have id information in the URL itself, so while the following routes would work:
/zip/networktool
/zip/1234
these routes would not:
/zip
/zip?id=networktool
/zip?id=1234
because Express is looking for the id to be built into the route itself. So you can do one of two things. You can either change your redirect to:
router.get('/download', function (req, res, next) {
res.redirect('/zip/'+ req.query.id);
});
or, you can modify your /zip route to make the id parameter optional with ?:
router.get('/zip/:id?', function (req, res) {
var fileName = (req.params.id) ? req.params.id : req.query.id;
});
I would recommend the first option, as the latter optional parameter only makes your zip route more complicated and require extra handling of whether id is actually passed to your route.
The path /zip/:id is expecting a path parameter not a query parameter.
You should redirect like this
res.redirect('/zip/'+ id);

I would like to add a query param apikey for all my external call

I have a server in express and it uses an external api. I would like for each request to that api ('/api/*'), that it appends a query param in the url without to write it for each requests.
app.use(function(req, res) {
req.query.key = process.env.APIKEY;
});
I tried something like that but it doesn't work.
I thought of doing something like :
app.get('/api/stuff', addApiKey, api.stuff);
Is there a better way?
You need to supply your middleware function with a next callback:
function addApiKey(req, res, next) {
req.query.key = process.env.APIKEY;
next();
});
app.get('/api/:endpoint', addApiKey, function(req, res) {
// do your stuff here
});

How do I redirect in expressjs while passing some context?

I am using express to make a web app in node.js. This is a simplification of what I have:
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
app.get('/', function(req, res) {
// Prepare the context
res.render('home.jade', context);
});
app.post('/category', function(req, res) {
// Process the data received in req.body
res.redirect('/');
});
My problem is the following:
If I find that the data sent in /category doesn't validate, I would like pass some additional context to the / page. How could I do this? Redirect doesn't seem to allow any kind of extra parameter.
There are a few ways of passing data around to different routes. The most correct answer is, of course, query strings. You'll need to ensure that the values are properly encodeURIComponent and decodeURIComponent.
app.get('/category', function(req, res) {
var string = encodeURIComponent('something that would break');
res.redirect('/?valid=' + string);
});
You can snag that in your other route by getting the parameters sent by using req.query.
app.get('/', function(req, res) {
var passedVariable = req.query.valid;
// Do something with variable
});
For more dynamic way you can use the url core module to generate the query string for you:
const url = require('url');
app.get('/category', function(req, res) {
res.redirect(url.format({
pathname:"/",
query: {
"a": 1,
"b": 2,
"valid":"your string here"
}
}));
});
So if you want to redirect all req query string variables you can simply do
res.redirect(url.format({
pathname:"/",
query:req.query,
});
});
And if you are using Node >= 7.x you can also use the querystring core module
const querystring = require('querystring');
app.get('/category', function(req, res) {
const query = querystring.stringify({
"a": 1,
"b": 2,
"valid":"your string here"
});
res.redirect('/?' + query);
});
Another way of doing it is by setting something up in the session. You can read how to set it up here, but to set and access variables is something like this:
app.get('/category', function(req, res) {
req.session.valid = true;
res.redirect('/');
});
And later on after the redirect...
app.get('/', function(req, res) {
var passedVariable = req.session.valid;
req.session.valid = null; // resets session variable
// Do something
});
There is also the option of using an old feature of Express, req.flash. Doing so in newer versions of Express will require you to use another library. Essentially it allows you to set up variables that will show up and reset the next time you go to a page. It's handy for showing errors to users, but again it's been removed by default. EDIT: Found a library that adds this functionality.
Hopefully that will give you a general idea how to pass information around in an Express application.
The easiest way I have found to pass data between routeHandlers to use next() no need to mess with redirect or sessions.
Optionally you could just call your homeCtrl(req,res) instead of next() and just pass the req and res
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
/////////////
// Routing //
/////////////
// Move route middleware into named
// functions
function homeCtrl(req, res) {
// Prepare the context
var context = req.dataProcessed;
res.render('home.jade', context);
}
function categoryCtrl(req, res, next) {
// Process the data received in req.body
// instead of res.redirect('/');
req.dataProcessed = somethingYouDid;
return next();
// optionally - Same effect
// accept no need to define homeCtrl
// as the last piece of middleware
// return homeCtrl(req, res, next);
}
app.get('/', homeCtrl);
app.post('/category', categoryCtrl, homeCtrl);
I had to find another solution because none of the provided solutions actually met my requirements, for the following reasons:
Query strings: You may not want to use query strings because the URLs could be shared by your users, and sometimes the query parameters do not make sense for a different user. For example, an error such as ?error=sessionExpired should never be displayed to another user by accident.
req.session: You may not want to use req.session because you need the express-session dependency for this, which includes setting up a session store (such as MongoDB), which you may not need at all, or maybe you are already using a custom session store solution.
next(): You may not want to use next() or next("router") because this essentially just renders your new page under the original URL, it's not really a redirect to the new URL, more like a forward/rewrite, which may not be acceptable.
So this is my fourth solution that doesn't suffer from any of the previous issues. Basically it involves using a temporary cookie, for which you will have to first install cookie-parser. Obviously this means it will only work where cookies are enabled, and with a limited amount of data.
Implementation example:
var cookieParser = require("cookie-parser");
app.use(cookieParser());
app.get("/", function(req, res) {
var context = req.cookies["context"];
res.clearCookie("context", { httpOnly: true });
res.render("home.jade", context); // Here context is just a string, you will have to provide a valid context for your template engine
});
app.post("/category", function(req, res) {
res.cookie("context", "myContext", { httpOnly: true });
res.redirect("/");
}
use app.set & app.get
Setting data
router.get(
"/facebook/callback",
passport.authenticate("facebook"),
(req, res) => {
req.app.set('user', res.req.user)
return res.redirect("/sign");
}
);
Getting data
router.get("/sign", (req, res) => {
console.log('sign', req.app.get('user'))
});
we can use express-session to send the required data
when you initialise the app
const express = require('express');
const app = express();
const session = require('express-session');
app.use(session({secret: 'mySecret', resave: false, saveUninitialized: false}));
so before redirection just save the context for the session
app.post('/category', function(req, res) {
// add your context here
req.session.context ='your context here' ;
res.redirect('/');
});
Now you can get the context anywhere for the session. it can get just by req.session.context
app.get('/', function(req, res) {
// So prepare the context
var context=req.session.context;
res.render('home.jade', context);
});
Here s what I suggest without using any other dependency , just node and express, use app.locals, here s an example :
app.get("/", function(req, res) {
var context = req.app.locals.specialContext;
req.app.locals.specialContext = null;
res.render("home.jade", context);
// or if you are using ejs
res.render("home", {context: context});
});
function middleware(req, res, next) {
req.app.locals.specialContext = * your context goes here *
res.redirect("/");
}
You can pass small bits of key/value pair data via the query string:
res.redirect('/?error=denied');
And javascript on the home page can access that and adjust its behavior accordingly.
Note that if you don't mind /category staying as the URL in the browser address bar, you can just render directly instead of redirecting. IMHO many times people use redirects because older web frameworks made directly responding difficult, but it's easy in express:
app.post('/category', function(req, res) {
// Process the data received in req.body
res.render('home.jade', {error: 'denied'});
});
As #Dropped.on.Caprica commented, using AJAX eliminates the URL changing concern.
Update 2021:
i tried url.format and querystring and both of them are deprecated, instead we can use URLSearchParams
const {URLSearchParams} = require('url')
app.get('/category', (req, res) =>{
const pathname = '/?'
const components ={
a:"a",
b:"b"
}
const urlParameters = new URLSearchParams(components)
res.redirect(pathname + urlParameters)
})
I use a very simple but efficient technique
in my app.js ( my entry point )
I define a variable like
let authUser = {};
Then I assign to it from my route page ( like after successful login )
authUser = matchedUser
It May be not the best approach but it fits my needs.
app.get('/category', function(req, res) {
var string = query
res.redirect('/?valid=' + string);
});
in the ejs you can directly use valid:
<% var k = valid %>

How to pass parameter to routes?

I am using Nodejs .
Server.js
app.get('/dashboard/:id', routes.dashboard);
Routes / index.js
exports.dashboard = function(req, res){
}
I want to be able to pass the 'id' variable from app.js to the dashboard function . How do I go about doing this ?
Assuming ExpressJS, you shouldn't need to pass it.
For each parameter placeholder (like :id), req.params should have a matching property holding the value:
exports.dashboard = function (req, res) {
console.log(req.params.id);
};
Though, this assumes the requested URL matches the route by verb and pattern.
Just ensure that your GET call from the client is something like this: /dashboard/12345.
12345 is the id you want to pass to dashboard.
So, you can access it like this in server:
exports.dashboard = function(req, res){
var id = req.params.id;
console.log('ID in dashboard: %s', id);
}

Dynamic routing depending on version number

I am working on a web api on node.js using the Express3 framework and I would want my routing to look something like /v0.1/function.
Ideally, the routing should automatically load the specified version module by looking at the major and minor version number given in the url. My approach so far is:
app.use('/v:major.:minor', function(req) {
return require('./v' + req.params.major + '.' +
req.params.minor);
});
And in my ./v0.1/index.js:
module.exports = function() {
var express = require('express'),
app = express();
app.get('/test', function(req, res) {
res.json({ success: true });
});
return app;
}();
If I call /v0.1/test now, it somehow does not match the route (404), but app.get('/v:major.:minor', function(req, res) { /* ... */ }); is working just fine.
How can I achieve this?
I think you want app.all (http://expressjs.com/api.html#app.all)
This will make is accessible for all HTTP Verbs and still have the variable routing working.
I tested it and it seems to work.
Edited as per comment
app.all('/v:major.:minor/:endpoint', function(req, res, next) {
var version_handler = require('./v' + req.params.major + '.' + req.params.minor);
if (!req.params.endpoint in version_handler) // endpoint_isn't supported at this version, 404
version_handler[req.params.endpoint](req, res, next);
});
You have to use app.param(). From the example in express API :
app.param('id', /^\d+$/);
app.get('/user/:id', function(req, res){
res.send('user ' + req.params.id);
});
You must provide the regular expression for your parameter in URL, since major, minor both are non negative integers /^\d+$/ should be it for you.
The Simplest way I found is chain a custom middleware:
Something like this:
app.use('/api/:version', function (req, res, next) {
console.log('The version name is : ', req.params.version)
req.version=req.params.version; //use this inside api router
next() // pass control to the next handler
},api);
api is your router object

Resources