I want to use express to create unique proxy instances using URLs that I am storing in a database. I found an npm module that may help with this called http-express-proxy but open to other solutions that uses express.
And I had a route like this (using http-express-proxy):
user.URL = 'https://www.google.com'
app.post('/', proxy(user.URL))
// and after this, user.URL is updated to a different value. I want the proxy's address to change too.
I did find a solution that dynamically creates a regular express route during runtime, but I cannot get it to work using the proxy() method from http-express-proxy:
https://alexanderzeitler.com/articles/expressjs-dynamic-runtime-routing/
According to that approach, I can require a 2nd file inside the POST route that looks like this: (and includes a call to the database using sequelize)
const express = require('express')
const proxy = require('express-http-proxy')
const User = require('../db/models/user')
const app = express()
module.exports= {
init : init
}
function init(app) {
User.findOne({where: {id: 1}})
.then(user => app.post('/', proxy(user.URL)))
}
And in my main app.js file, I am then doing this:
// ...
app.post('/', function(req, res, next) {
var dynamic = require('./dynamic')
dynamic.init(app)
})
// ...
But I am getting a 503 response when I post using this approach using http-express-proxy, which was not used in his example.
I got it to work like this. I modified the express-http-proxy module and at the top of the SendProxyRequest() method I included this:
if (req.URL) host = req.URL
And then in my app.js file I added a middleware method to set req.URL after a call to the database:
function getProxyURL (req, res, next) {
User.findOne({where: {id: 1}})
.then(user => {
if(user.URL) req.URL = user.URL
next()
})
}
app.post('/', getProxyURL, proxy('www.default.com'))
Related
I have the following route in my express (version 4.17.1) API in a postTimecardCompany.js file:
const mongoose = require('mongoose');
const Timecard = require('./../models/timecard');
function postTimecardCompany(server) {
server.post('/api/v1/timecard/:userId', (req, res) => {
// Insert timecard data into the database
Timecard.create(data, (error, result) => {
// Check for errors
if (error) {
res.status(500).end();
return;
}
// Respond
res.status(200).send({timecardId: result._id});
});
});
}
module.exports = postTimecardCompany;
The route (among other routes) is loaded via the following mechanism by server.js file:
[
'postTimecardCompany',
'anotherRoute',
'someOtherRoute',
'andSoOn...'
].map((route) => {
require('./core/routes/' + route + '.js').call(null, server)
});
I have a middleware (in server.js file) where I check which route is being called.
server.use((req, res, next) => {
// If route is "/api/v1/timecard/:userId" do something
});
I have found various solutions which do nearly what I am looking for, but not exactly.
For example, if I post to the route with a data parameter userId value of "123f9b" then req.originalUrl gives an output of "/api/v1/timecard/123f9b."
What i'm looking to get is the original route path with the parameters in it so for a request of "/api/v1/timecard/123f9b" it would be: "/api/v1/timecard/:userId."
How do I get this functionality in express or extend express to get the original route path with parameters in the request object?
if you want to use from your approach, it's is impossible, after that your approach is not standard in express check the documentation, if you want get routes in a middleware you should try like this:
server.js
const express = require('express')
const server = express()
const postTimecardCompany = require('./routes/postTimecardCompany.js')// don't use map()
server.use("/",postTimecardCompany)//use the routes
server.listen(6565,()=>console.log(`Listening to PORT 6565`))
routes of postTimecardCompany.js
use Router of express and export router, and you can use middleware before each route you want, there are many ways to use middleware in routes, check the documentation
const express = require("express");
const router = express.Router();
const middleware = require('../middleware');//require middlewares
router.post("/api/v1/timecard/:userId", middleware,(req, res) => {
// Insert timecard data into the database
console.log(req.route.path);
});
module.exports = router;
middleware.js
module.exports = ((req, res, next) => {
console.log(req.route.path);
next()
});
Not a pro with things like server middleware, but stuck without much clues.
In a vue component I am retrieving data with axios with this:
axios
.get('/api/getDailyFeedback', {
params: {
start: '2018-05-01'
}
})
Which goes to the express (version 4.16.2) server middleware setup in Nuxt. I have gotten normal get and post requests to work fine but the problem is when I want to pass in a parameter into a get request like the below:
router.get('/getDailyFeedback', (req, res) => {
console.log('Query:', req.query.start);
//do stuff
});
What little experience I had with Express 4, it took me a little bit of time to realise why parameters passed in the body of a post request were undefined, but it was because I needed to add body-parser.json() to my nuxt config since they removed that from the main express package. Similarly I thought I needed bodyParse.urlencoded but that has not worked. This is the line I added to my nuxt.config:
serverMiddleware: [bodyParser.urlencoded({ extended: false }), bodyParser.json(), '~/api'],
I am unsure if the content type is not set correctly or I am missing something simple here. I know I am able to use various libraries to grab the parameters from the url string which I have access to, as Axios is working as expected and adding my 'params' object onto the end of my get request. But a lot of solutions I have seen to other problems is 'oh its as simple as accessing req.query' Alas Express fails to define a req.query object, or even req.params or req.body.
Hopefully that is enough detail to go on, thanks a lot for reading and any suggestions.
Well that's an unpleasant surprise to try to use the get request in Nuxt for the first time, isn't it? Nuxt creates a connect instance which leads to some pitfalls. It's req is the Node.js http request object, so it doesn't have the query property and request.body is also being skipped in get requests!
But there is a way for everything. Your code:
axios.get('/api/getDailyFeedback', {
params: {
start: '2018-05-01'
}
})
Translates to the following URI call:
/api/getDailyFeedback?start=2018-05-01
So you've got all the params in the URI where you can retrieve them from via url parsing. Module url ships with Node.js by the way.
const url = require("url");
router.get('/getDailyFeedback', (req, res) => {
let queryData = url.parse(req.url, true).query
console.log('Query:', queryData.start)
});
I wrote a little helper that extends req with a custom property. Here's the code:
router.use((req, res, next) => {
var theQuery = url.parse(req.url, true).query
req.queryData = theQuery
next()
})
Then you can use it like this:
router.get('/getDailyFeedback', (req, res) => {
console.log('Query:', req.queryData.start)
});
Other way to pass params via get is by using optional uri segments:
router.get('/getDailyFeedback/:start?', (req, res) => {
console.log('Query:', req.params.start)
});
I am using Nuxt 2.15 and I can access the query parameter like this req._parsedOriginalUrl.query
You can access Nuxt get params without additional modules.
Just use:
req.request.get()
For anyone else in the future
const { Router } = require('express')
Router.get('/orders', async (req, res, next) => {
const request = req.query.sometext;
res.json({data: request});
});
module.exports = Router;
<script>
export default() {
async mounted() {
const orders = await axios.get(`/api/orders`, {
params: {
sometext: 'bla bla'
}
});
}
}
</script>
http://expressjs.com/en/api.html#req
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, ... });
});
});
});
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 %>
I am using parse.com cloud code with express to setup my routes. I have done this in the past with node, and I have my routes in separate files. So, in node I do
app.js
express = require("express");
app = exports.app = express();
require("./routes/js/account");
account.js
app = module.parent.exports.app;
app.get("/api/account/twitter", passport.authenticate("twitter"));
All the examples on parses site https://parse.com/docs/cloud_code_guide#webapp show this being done as follows.
app.js
var express = require('express');
var app = express();
app.get('/hello', function(req, res) {
res.render('hello', { message: 'Congrats, you just set up your app!' });
});
So, I would like to change the bottom to include a routes folder with separate routes files, but am not sure how to do this in parse.
I know this post is a little old, but I just wanted to post a solution for anyone still looking to get this to work.
What you need to do, is create your route file, I keep them in 'routes' forlder, for example <my_app_dir>/cloud/routes/user.js
Inside user.js you will have something that looks like this:
module.exports = function(app) {
app.get("/users/login", function(req, res) {
.. do your custom logic here ..
});
app.get("/users/logout", function(req, res) {
.. do your custom logic here ..
});
}
Then, in app.js you just include your file, but remember that you need to append cloud to the path, and pass the reference to your app instance:
require('cloud/routes/user')(app);
Also, remember that express evaluates routes in order, so you should take that into consideration when importing several route files.
I'm using a different method, have the routes in app.js, but you can probably include them in file if you prefer. Take a look at the example app,
anyblog on github
The way it works:
Set up a controller:
// Controller code in separate files.
var postsController = require('cloud/controllers/posts.js');
Add the controller route
// Show all posts on homepage
app.get('/', postsController.index);
// RESTful routes for the blog post object.
app.get('/posts', postsController.index);
app.get('/posts/new', postsController.new);
And then in posts.js, you can use exports, ex.
var Post = Parse.Object.extend('Post');
// Display all posts.
exports.index = function(req, res) {
var query = new Parse.Query(Post);
query.descending('createdAt');
query.find().then(function(results) {
res.render('posts/index', {
posts: results
});
},
function() {
res.send(500, 'Failed loading posts');
});
};
// Display a form for creating a new post.
exports.new = function(req, res) {
res.render('posts/new', {});
};
Pass the app reference to the post controller, and add the routes from there