I want to pass the environment for Express to a routing module for Express. I want to key off of whether Express is running in development or production mode. To do so, I'm guessing I need to pass app.settings.env somehow to a routing module.
My routing module exports a function for each route. So:
app.get('/search', web.search);
Based upon a previous stackoverflow post, i have tried this:
var web = require('./web')({'mode': app.settings.env});
But node throws an type error (object is not a function).
I'm new to Node and Express. Can I pass a value to an express route and if so, how?
If you web.js looks like this:
module.exports.search = function(req, res, next) {
// ...
};
module.exports.somethingOther = function(req, res, next) {
// ...
};
then by calling
var web = require('./web')({'mode': app.settings.env});
you try to use object (module.exports) as function. Type error here.
You need to convert module.exports to function to pass parameters to it. Like this:
module.exports = function (env) {
return {
// env available here
search: function(req, res, next) {
// ...
},
somethingOther: function(req, res, next) {
// ...
};
};
};
Related
I use nestjs to build a REST API.
I have a middleware which loads data from redis cache and should save it in the request object to access it in the controller function.
If i use express as engine it works, but with fastify it doesn't work. The data is undefined in the controller function.
The code looks like:
function mymiddleware(req, res, next) => {
req.data = {...};
next();
};
this is a simple working example:
const fastify = require('fastify')({ logger: true })
fastify.use(function (req, res, next) {
console.log('middy')
req.data = { hello: 'world' }
next();
})
fastify.get('/', (req, res) => {
res.send(`hello ${req.raw.data.hello}`)
})
fastify.listen(3000)
I think that your problem is due to the req object: in middleware (registered using .use you will get the standard Node.js request, instead of augmented HTTPRequest in the fastify handler.
So, you can access the low-level Http request with .raw field.
Introduction
So far I have three files, one test.js is a file where I have built three functions that work.
But now I am trying to structure using MVC or at least some pattern. So now I router.js and app.js
Question
Should I put my promise functions from test.js in my config.js or server.js or something else, Im just interested in how people would do this and whats the correct way of structuring NodeJS.
server.js
In here start the server and apply the routes to my app
var configure = require('./router');
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
// get an instance of router
var router = express.Router();
configure(router);
app.listen(port);
console.log('Server has started!! ' + port);
// apply the routes to our application
app.use('/', router);
config.js
In here I build my routes
module.exports = function (router) {
// route middleware that will happen on every request
router.use(function (req, res, next) {
// log each request to the console
console.log(req.method, req.url);
// continue doing what we were doing and go to the route
next();
});
// home page route (http://localhost:8080)
router.get('/', function (req, res) {
res.send('im the home page!');
});
// sample route with a route the way we're used to seeing it
router.get('/sample', function (req, res) {
res.send('this is a sample!');
});
// about page route (http://localhost:8080/about)
router.get('/about', function (req, res) {
res.send('im the about page!');
});
// route middleware to validate :name
router.param('name', function (req, res, next, name) {
// do validation on name here
console.log('doing name validations on ' + name);
// once validation is done save the new item in the req
req.name = name;
// go to the next thing
next();
});
// route with parameters (http://localhost:8080/hello/:name)
router.get('/hello/:name', function (req, res) {
res.send('hello ' + req.params.name + '!');
})
// app.route('/login')
// show the form (GET http://localhost:8080/login)
.get('/login', function (req, res) {
res.send('this is the login form');
})
// process the form (POST http://localhost:8080/login)
.post('/login', function (req, res) {
console.log('processing'); // shows on console when post is made
res.send('processing the login form!'); // output on postman
});
};
test.js
In here is a list of functions that are a chain of promises getting data and API Keys
(small function, one of many that feed into each over)
var firstFunction = function () {
return new Promise (function (resolve) {
setTimeout(function () {
app.post('/back-end/test', function (req, res) {
console.log(req.body);
var login = req.body.LoginEmail;
res.send(login);
resolve({
data_login_email: login
});
});
console.error("First done");
}, 2000);
});
};
My recommended structure is to put everything except server.js in lib directory so all your app is lib/ plus server.js - everything else is package.json, dependencies in node_modules (created on npm install, not in the repo), .gitignore, config files for Travis, Circle, Heroku or whatever service you're using, some README.md and things like that.
Now, server.js is just bare minimum that requires lib/app:
const app = require('./lib/app');
and starts the server with something like:
const server = app.listen(app.get('port'), () => {
logger.info('%s listening on port %s', app.get('name'), app.get('port'));
});
server.on('error', (err) => {
logger.error(err.message || err);
process.exit(1);
});
where logger is some higher lever logger like Winston or something like that.
That's it. Now, lib/app.js is minimum code that loads the middleware like body parsers etc., creates the express app and sets the variables for port and name and then uses a router that is exported by lib/routes:
const routes = require('./routes');
// ...
app.use('/', routes);
The lib/app should be enough to use for testing with tools like supertest but it doesn't listen on any port - server.js does. This is important to simplify testing.
The router exported by lib/routes is used for everything and you can start with a single lib/routes.js file and then convert it to lib/routes/index.js plus several files in lib/routes as needed.
The routes only define the actual routes and input validation with a module like e.g. express-validation and register controllers that are exported by lib/controllers - that can start as lib/controllers.js and get converted to lib/controllers/index.js plus lib/controllers/*.js as needed - just like the routes.
Then I would add top level spec or test or tests directory where all of the tests go. The tests can require your lib/app to run the tests on it with no need to listen on actual TCP ports - those will test your routes with actual controllers. Other tests will require lib/util and run some unit tests on your utilities etc. Make sure to use a tool like istanbul or nyc to calculate the test coverage.
The database schemas and data models would go to lib/schemas and lib/models, some utility helpers in lib/util, some config loading code in lib/config etc.
This is quite flexible layout and works pretty well. You can start with just few files:
README.md
LICENSE.md
package.json
server.js
lib/app.js
lib/routes.js
lib/controllers.js
lib/config.js
etc. and easily convert all of the xxx.js file into xxx/index.js with entire folder of smaller xxx/*.js files as needed.
The main difference from your approach is that I recommend exporting routers and using them by higher level routers instead of passing the high level router into lower lever modules that export functions that take routers to work on.
So instead of:
const moreSpecific = require('more-specific');
const moreGeneral = express.Router();
moreSpecific(moreGeneral);
and then in more specific:
module exports = (router) => {
router.use('/abc/xyz', ...);
};
I would recommend exporting a more specific router in a file e.g. routes/abc.js:
const router = express.Router();
router.use('/xyz', ...);
module exports = router;
and then in more general router e.g. in routes/index.js:
const abc = require('abc');
const router = express.Router();
router.use('/abc', abc);
// and export the main router for other modules like app.js to use:
module.exports = router;
to have a route like /abc/xyz.
I'm using a module called consign to include several different modules in a directory at once (instead of having a bunch of require statements). Within these modules, I've been setting the mount path for each endpoint at the top of the router file so as not to repeat it several times throughout the file. However, consign passes the same router to each of these (which should normally be fine) and the mount path is actually being overwritten via the use() method if the path is the same in any of the files. I'll try and show this the best way I can...
/routes/api.js
var express = require('express');
var router = express.Router();
var consign = require('consign');
// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({
cwd: 'routes'
})
.include('api')
.into(router);
module.exports = router;
/routes/api/player.js
module.exports = function (router) {
router.use('/player', router);
router.get('/getInfo', function (req, res, next) {
res.error = false;
res.data = {};
res.message = "Player getInfo API Call - IN DEVELOPMENT";
next();
});
};
/routes/api/profile.js
module.exports = function (router) {
router.use('/profile', router);
router.get('/getInfo', function (req, res, next) {
res.error = false;
res.data = {};
res.message = "Profile getInfo API Call - IN DEVELOPMENT";
next();
});
}
Consign is loading in the modules just fine, but the router.use() method seems to be overwriting the callbacks when the paths are the same (disregarding the base path that is). For instance, both "/player/getInfo" and "/profile/getInfo" work as a call, but are both responding with "/profile/getInfo" data.
BTW - in case you're wondering and in case it's pertinent, I have a small piece of middleware called "formatResponse" that will take the data and format all of the calls in the same way, which is why I have a "next()" instead responding from the function itself. The code for that is below as well.
/middleware/formateResponse.js
module.exports = function(req, res, next) {
res.json({
error: res.error,
data: res.data,
message: res.message
});
}
The way you're doing it right now, there's no scope. The fact that you mounted the router on '/profile' and then added a get statement to the '/getInfo' path doesn't really change the scope the way you think it does. They're both stored to match on '/getInfo', with the last one in winning (regardless of prefix. I bet navigating to http://your-server/getInfo will work too).
You either need to use a different router for each module (and then mount that one on the path root you want) or else be more explicit in the rest of the routes (e.g. call router.get('/profile/getInfo', ...).
I have an app where I am trying to remove the hashbang ( ! and #) prefixes for my routes, but still have people be able to use bookmarked routes. For the most part I have been able to get it work with html5Mode set to true, but there are a few cases where it is not working. Here is how I have my server configured:
var router = require('./router')(app);
app.use(express.static(path.join(__dirname, '../client')));
app.get('*', function (req, res, next) {
res.sendFile('index.html', {root:'../client/app/'});
});
router in this case looks like this:
var express = require('express');
var router = express.Router();
var Products = require('../../database').Products;
router.get('/:flavor', function (req, res) {
var flavor = req.params.flavor;
Products.findOne({flavor:flavor}, function (err, product) {
if (err) {
throw err
}
res.json(product);
});
Getting the flavor routes, is one case where this setup does not work. If someone directly types into the browse, mysite.com/lemon they receive the JSON data back, only (no template or anything). Normally this is used by the angular app, (which would typically make the request to /lemon and implement it into the template). However, if I move the router below the app.get('*'), then any request made by Angular for the data is returned with the entire index.html page. How can I make it so that a direct request by the browser for a route that normally returns JSON sends the index file?
I am using an express.js package called express-subdomain to facilitate requests to defined subdomains I set up.
As far as I understand, the subdomain constructor function expects an express router object which I pass to it from an exported router module.
What I have tried is as follows:
MAIN APP.JS SERVER FILE
var common = {
express: require('express'),
subdomain: require('express-subdomain')
};
common.app = common.express();
module.exports = common;
common.app.listen(3000, function () {
console.log(('app listening on http://localhost:3000'));
});
var router = require('./router/index');
// Error Handling
common.app.use(function(err, req, res, next) {
res.status(err.status || 500);
});
router/index
module.exports = function (){
var common = require('../app');
var router = common.express.Router();
common.app.get('/', function(req, res) {
res.send('Homepage');
});
common.app.use('/signup', require('./routes/signup'));
common.app.use(common.subdomain('login', require('./routes/login')));
}();
routes/login
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {
res.send('login working');
});
router.get('/info', function (req, res) {
});
module.exports = router;
I have tried to access the login subdomain at the following urls:
http://login.localhost
http://login.localhost:3000
http://login.localhost.com
http://login.localhost.com:3000
Any clarification or assistance appreciated.
author of express-subdomain here 👋
A couple of things:
Hosts must be setup correctly - I'd recommend something like so in your /etc/hosts file.
127.0.0.1 myapp.local
127.0.0.1 login.myapp.local
For more information on this see https://github.com/bmullan91/express-subdomain#developing-locally
Register the subdomain routes before any others, including the homepage route. The order is very important
The pattern you're using in /routes/index.js is not advised (requiring a self invoking function). Exporting the Router like you done in /routes/login.js is cleaner.
Finally, If you're still stuck take a look at the source for express subdomain and in particular its tests.
Happy coding.