Pass navigation variables to router in express - node.js

So I'm trying to pass in variables through the navigation bar for my router.
I'm using Node.js with express and mongolab for the database for this.
Here is the working code:
router.get('/sort/50/time', function(req, res) {
var db = req.db;
var collection = db.get('twots');
collection.find({},{'skip':0, 'limit':50, 'sort':{_id: -1}},function(e,docs){
res.json(docs);
});
});
It gives me a database list from mongolab.
But I want to do something like this:
router.get('/sort:VARIABLE2:VARIABLE1', function(req, res) {
var db = req.db;
var collection = db.get('twots');
collection.find({},{'skip':0, 'limit':req.params.VARIABLE2, 'sort':{req.params.VARIABLE1: -1}},function(e,docs){
res.json(docs);
});
});
This doesn't work, and can't find the correct code for this. (it can't find req.params.VARIABLE1 or VARIABLE2)
Right now I've coded the hard way, using no variables.

You still have to include the slashes:
router.get('/sort/:VARIABLE2/:VARIABLE1', function(req, res) { ... })

Related

Mongo Query Returning Empty Array

I'm a total beginner at this and am using a tutorial to learn the basics of the MEAN stack. I am trying to return the documnents in my database to a web page but am instead receiving an empty array.
I have created a cluster on Mongodb Atlas called mytasklist. Inside here I created a database called mytasklistdb. Inside this I have a table (object) called mytasklistdb.mytasklisttutorial. My understanding of this is limited and so maybe I'm making a huge error somewhere here. I have experience of SQL but not Mongo and so the whole 'clusters' and 'collections' thing is new to me.
Anyway my code is as follows. I took the string for the database connection from the Mongo connection tab.
var express = require('express');
var router = express.Router();
var mongojs = require('mongojs');
var db = mongojs('mongodb+srv://myusername:mypassword#mytasklist-qx0ka.mongodb.net/test?retryWrites=true&w=majority', ['mytasklisttutorial']);
router.get('/tasks', function(req, res, next){
db.mytasklistdb.find(function(err, tasks){
if(err){
res.send(err);
}
res.json(tasks);
});
});
module.exports = router;
My database objects look like this:
_id:5db5f1f31c9d440000c3e7fe
title:"Walk the dog" - this is a string
isDone:false - this is boolean
I'm just getting an empty array but in the tutorial the guy is getting these 'documents'. What am I doing wrong?
EDIT: I realised that the 'tasks' part of the tutorial example was relating to a database called 'tasks'. Mine is called 'mytasklistdb'. I therefore changed this. I also added a parameter with the name of my collection to the line passed in to mongojs.
I have changed my code above to reflect this
The solution was to replace 'task' and 'test' with the name of my db. As follows:
var express = require('express');
var router = express.Router();
var mongojs = require('mongojs');
var db = mongojs('mongodb+srv://James:Noentry1#mytasklist-qx0ka.mongodb.net/mytasklistdb?retryWrites=true&w=majority', ['mytasklisttutorial']);
router.get('/tasks', function(req, res, next){
db.mytasklisttutorial.find(function(err, tasks){
if(err){
res.send(err);
}
res.json(tasks);
});
});
module.exports = router;
My guess is that you are not passing your query, just the callback in the find() method, probably you need to do something like this:
db.tasks.find({},function(err, tasks){
if(err){
res.send(err);
}
res.json(tasks);
});

Easiest way to pass variables to routes templates in Express?

I've just made an Node.js app modular by splitting up data models and routes into separate files.
My routes are exported by express.Router(). In these routes I would like to import queried values from my app.js to be rendered with the templates.
How would I in the easiest way save things lets say with app.locals or req.variableName?
Since the route using express.Router() ties it together with app.js, should I be using app.params() and somehow make these values accessible?
Using globals seems like a worse idea as I'm scaling up the app. I'm not sure if best practice would be saving values to the process environment either using app.locals.valueKey = key.someValue...
Big thanks in advance to anyone
If I understand the question correctly, you want to pass a value to a later middleware:
app.js:
// Let's say it's like this in this example
var express = require('express');
var app = express();
app.use(function (req, res, next) {
var user = User.findOne({ email: 'someValue' }, function (err, user) {
// Returning a document with the keys I'm interested in
req.user = { key1: value1, key2: value2... }; // add the user to the request object
next(); // tell express to execute the next middleware
});
});
// Here I include the route
require('./routes/public.js')(app); // I would recommend passing in the app object
/routes/public.js:
module.export = function(app) {
app.get('/', function(req, res) {
// Serving Home Page (where I want to pass in the values)
router.get('/', function (req, res) {
// Passing in the values for Swig to render
var user = req.user; // this is the object you set in the earlier middleware (in app.js)
res.render('index.html', { pagename: user.key2, ... });
});
});
});

node.js export variables across files

I'm relatively new to web development, brand new to node.js.
I'm trying to use express to make a site that updates an api with the chef-node api client, using this tutorial
If I create a stand alone node.js app, it works as expected, and the value 'bacon' is set to 'good'
app1.js
var fs = require('fs'),
chef = require('chef'),
key = fs.readFileSync('/Users/foo.pem'),
chef_client = chef.createClient('foo', key, 'https://chef.example.com/organizations/dev');
var mybreakfast = { "id":"food","bacon":"good"}
chef_client.put('/data/breakfast/food', mybreakfast, function(err,res,body) {
if (err) { return console.log(err); }
console.log(body)
});
Full code is here.
The app basically looks like this:
app.js
var fs = require('fs'),
chef = require('chef'),
key = fs.readFileSync('/Users/foo.pem'),
chef_client = chef.createClient('foo', key, 'https://chef.example.com/organizations/dev');
...
//tutorial says this isn't ideal, but it is quickest way to get working
app.use(function(req,res,next){
req.client = chef_client;
next();
});
routes/index.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/databags', function(req, res) {
req.client.get('/data/breakfast/food', function(err,res,body) {
if (err) { return console.log(err); }
console.log("Found breakfast")
console.log(body)
bag_data = body; //TODO: Global variable is bad and you should feel bad, how do I use correct variable?
});
res.render('databags.jade', { title: 'Databags', somedata: bag_data });
});
router.post('/databags', function(req, res) {
var mybreakfast = { "id":"food","bacon":"good"}
req.client.put('/data/breakfast/food', mybreakfast, function(err,res,body) {
if (err) { return console.log(err); }
console.log(body)
});
});
vies/databags.jade
html
body
form(action='/databags', id='derp', method='POST')
each value, key in somedata
label #{key}
input(type='text',name="#{key}", value="#{value}")
br
br
input(type='submit', value='Submit', form='derp')
When I press the submit button in /databags, I get a 301 and no data is uploaded.
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>openresty/1.7.10.1</center>
</body>
</html>
I suspect the problem has to do with the fact that I'm adding client to every request.
//tutorial says this isn't ideal, but it is quickest way to get working
app.use(function(req,res,next){
req.client = chef_client;
next();
});
What is the proper way to make the chef_client variable in app.js available in routes/index.js?
If that is the correct way to do this, then what could be making the app work by itself, but not when used with the express framework?
Since this is my first node.js / express site, any other suggestions to get this app working would be very appreciated.
Update
Solution has been found, working code snippet available here: https://gist.github.com/spuder/1e39868b6a9a0c3cdb13
If route_index.js is your routes/index.js then your problem is that you're not actually exporting anything from it. When you require('routes/index') you're going to get back whatever you set module.exports to to in that file.
That means your routes/index.js file should end with:
module.exports = router;
To address your question regarding how to share chef_client without a global, you can return a factory from routes/index.js that returns an instantiated router using the parameters you pass to it. That would look something like this:
routes/chef.js
function addChefRoutes(router, chefClient) {
router.get('databags', function(res,req){
// ...
}
});
module.exports = addChefRoutes;
app.js
var chefClient = chef.createClient('foo', key, 'https://chef.example.com/organizations/dev');
var addChefRoutes = require('./routes/chef');
var router = express.Router();
addChefRoutes(router, chefClient);

How to get parameters using express in node

I am certain that this is super easy, but I can't for the life of me figure out why this does not work.
So trying to write an application where a user can search, gets a link, and if clocked it calls a second route, passing a variable while it is at it. Sounds simple, so thought I.
The idea is that each link generated gets a link like "localhost:3000/getcan/:[id]"
So for our example, I am trying to get 22 into a variable if I try to go to webpage
"localhost:3000/getcan/:22"
To set up the route, I set the following in app.js
app.use('/getcan/:*', getcan);
This seems to work, and if I put anything that calls /getcan/: I go to the right route.
The route it self looks as follows
var express = require('express');
var router = express.Router();
/* GET getcan page. */
router.get('/', function(req, res, next) {
var canid = req.params.canid;
console.log("Got the following id " + canid)
res.render('getcan', { title: 'ResourceEdge', reqcan: canid });
});
module.exports = router;
I think the problem is with router.get('/' but if I make any changes (tried get('/getcan/:canid) it all blows up with a 404.
Any pointers?
Change app.use to:
app.use('/getcan', getcan);
And your router to:
/* GET getcan page. */
router.get('/:canid', function(req, res, next) {
var canid = req.params.canid;
console.log("Got the following id " + canid)
res.render('getcan', { title: 'ResourceEdge', reqcan: canid });
});
Then call your route using: http://localhost:3000/getcan/22

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 %>

Resources