I've been toying with NodeJS and Express for a few days, and it is pretty cool. But I've run into an issue I cannot get my head around, so I'm hoping someone else out there can help me clear this up.
I know that I can declare a module, and then pass parameters into this module via the require method.
--- App.js ----
var foo = require('./modules/Foo.js');
foo.config();
var bar= {}
require('./modules/Bar.js)(bar, foo)
--- Bar.js ---
module.exports = function(Bar, Foo) {
Bar.act = function(){
Foo.do();
}
}
I think this is a much cleaner design than having to require Foo in every module that would require using it. I also don't have to do initialization in each module that requires it.
But then comes the Router, and I can't really understand how it works. Most examples out there requires the router in each module, something like this:
--- App.js ----
var index = require('./views/index.js');
app.use('/', index);
--- Index.js ---
var express = require('express');
var router = express.Router();
var foo = require('../modules/Foo.js);
foo.config();
router.get('/', function (req, res) {
foo.do();
res.render('index');
})
module.exports = router
But then, I need to require all modules used in index.js manually. What I would really want to do is something like this:
--- App.js ----
var foo = require('../modules/Foo.js);
foo.config();
var index = require('./views/index.js')(foo);
app.use('/', index);
--- Index.js ---
var express = require('express');
var router = express.Router();
module.exports = function(foo){
router.get('/', function (req, res) {
foo.do();
res.render('index');
})
}
But writing it this way, the compiler tells me "Router.use() requires middleware, got a undefined". I can sorta see where this error comes from, (the module.exports does not return an object) but so how do I structure my code so that I can pass parameters to a module that in the end will export a Router middleware? I want to require and initialize Foo in one place, then pass it to all router modules that uses it. Is it possible somehow?
You almost got it, you just need to return the Router.
A fixed index.js could look like this:
var express = require('express');
module.exports = function(foo){
var router = express.Router();
router.get('/', function (req, res) {
foo.do();
res.render('index');
});
return router;
}
Notice the additional ; and the return.
I've also moved the instantiation of the router to the function. In node modules are cached, you can require the same module multiple times and they will all share their global variables. This can lead to unexpected results. In your case you'll overwrite the router.get handler every time and the same instance of Foo would be used for every request.
Related
Im setting up a web app in express.js and to keep everything organized i want to keep each route on it's own separate file and keep those files in two folders, one for GET and one for POST. How would i go about doing this?
I want to have a main server.js file and a folder with two subfolders one for GET and one for POST that contain one file for each route
Heres a diagram:
server.js
routes/
├── get/
│ ├── login.js
│ ├── register.js
│ └── home.js
└── post/
├── login.js
└── register.js
Using express.Router() will allow you to seperate your handlers into different files. you can simply include them in your main server.js
simply export the custom router.
i would recommend keeping related functionality together rather than splitting the get and post routes into different directories. for example you might have users.js which handles user related tasks and another documents.js for documents etc. you may also add an app to an app. and routes to other routes.
try
$ npm install -g express-generator
$ express-generator
example:
server.js
var route1 = require('./routes/route_1'); // Include custom router 1
var route2 = require('./routes/route_2'); // Include custom router 2
var app = express();
app.use('/route1', route1);
app.use('/route2', route2);
route_1.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) { // handle specific get/post/etc methods
...
});
module.exports = router;
route_2.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
...
});
module.exports = router;
A Domain Driven Design Approach
So principles of DDD (Domain Driven Design) will encourage keeping things that belong together, together (in this case in Domain specific folders). However, I agree with #Fattie in that keeping all the routes together is better. For example, your NodeJS project may be an API and you can easily see all the endpoints served. This isn't breaking DDD. But here's how you can keep it lean:
# routes.js
const express = require('express');
const bodyParser = require('body-parser');
const { feature } = require('./router-utils');
const router = express.Router();
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
// Easy to see the route and what feature controls it.
router.get('/api/foo/bar/:something', feature('foo/bar'));
router.get('/api/foo/baz/:something', feature('foo/baz'));
router.get('/api/blah/blaaaah/:something', feature('blah/whatever'));
module.exports = router;
# router-utils.js
const cookie = require('cookie');
const auth = require('../auth/authorization');
const feature = (name, data = {}) => async (request, response, next) => {
// Do middleware stuff with cookies
const cookies = cookie.parse(request.headers.cookie || '');
// Do middleware stuff with payloads
if (request.url.includes('/some/path/here') && request.body?.payload) {
request.body = JSON.parse(request.body?.payload);
}
const { something } = request.body || '';
// Exit and continue to next routes if something is wrong.
if (something_wrong_here) {
next();
return null;
}
const Feature = require(`../features/${name}/${name.split('/').pop()}`);
// Pass all data to the feature.
return new Feature({ request, response, next, data }).handle();
};
module.exports = { feature };
# /features/foo/bar.js
const Query = require('../../common/query');
class Bar extends Query {
constructor(props) {
// Attach the request object to "this.request"
super(props);
}
async handle() {
let response = {};
const something = this.request.query.something;
// make some REST call to an upstream service and return the response.
// or make some database call and return the response.
}
module.exports = Bar;
An even simpler approach, just using the abilities of Node/js:
Say you have this:
app.use('/auth', async function (req, res, next) {
.. code
.. code
.. code
.. code
.. code
.. code
})
Recall you can simply do this:
app.use('/auth', someFile.someFunction {
I find this to be a very easy and clean approach. It's arguably better to have all the actual routes together in one place to look at. It may be useful in some projects.
Keep getting this error, whilst trying to run my app. Low level of knowledge have tried many solutions online to fix this, but nothing has got me up and running. Here is the Error-
Error: Route.get() requires a callback function but got a [object Undefined]
at Route.(anonymous function) [as get] (d:\dev_portal\bit_racer_real\alpha_bit_racer\node_modules\express\lib\router\route.js:202:15)
index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.redirect('/catalog');
});
module.exports = router;
catalog.js
var express = require('express');
var router = express.Router();
// Require controller modules.
var horse_controller = require('../controllers/horseController');
router.get('/', horse_controller.index);
module.exports = router;
app.js
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var catalogRouter = require('./routes/catalog');
var app = express();
var mongoose = require('mongoose');
var mongoDB = 'mongodb://admin:bitracer33#ds123753.mlab.com:23753/alpha_bit_racer';
mongoose.connect(mongoDB);
mongoose.Promise = global.Promise;
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/catalog', catalogRouter);
module.exports = app;
Not exactly sure where I have gone wrong, I have also heard that I may have a call in my routes index.js file in express. The call doesn't exist. I have spent hours going over the code making small changes, trying to get this simple error to clear :(
Has anyone else experienced this problem. Id love to get a clear answer.
as far as I can see, your index.js file is in the horsecontroller directory.
But when you use CommonJS's require with a directory instead of a file, it automatically defaults that to ../path/to/directory/index.js, so you don't have to specify horse_controller.index in the app.get function, instead just specify horse_controller.
The final code would look like this:
var express = require('express');
var router = express.Router();
// Require controller modules.
var horse_controller = require('../controllers/horseController');
router.get('/', horse_controller);
module.exports = router;
Like Daksh said, in the line router.get('/', horse_controller.index); (file catalog.js) horse_controller.index is not a function.
In your catalog.js you require your controller lke this require('../controllers/horseController') that means you have horseController.js file or horseController is a directory and in which you have an index.js file. After requiring it you save it in horse_controller variable and when you register It as an request handler you specify horse_controller.index
This presume you have exporting in your horseController an object like this
module.export = {
index: function(request, response) {
// Your code goes here
}
}
but if it isn't the case that why your are getting that error
Error: Route.get() requires a callback function but got a [object Undefined]
This code means you have heighter you are exporting nothing in your ../controllers/horseController file or ../controllers/horseController/index.js
If the index.js file under the horseController directory you can pass the horse_controller variable directly as an request handler to the app.get like this
app.get('/', horse_controller);
Passing it directly because in the index.js file you are exporting the Express Router directly
I got this resolved.
I was missing .index function from controller .js file.
added this line
exports.index = function(req, res) {
res.send('NOT IMPLEMENTED: Site Home Page');
};
now .index is no longer looking for an object.
Thanks for the help, appreciate the responses. :)
I am playing with node.js and I don't quite understand why something I set up is working in one instance but if I make a slight change it will not work in another instance.
in my app.js I have
app.use('/musicplayer', require('./routes/music/index'));
in my music\index.js I have
var express = require('express');
var router = express.Router();
router.use('/users', require('./users'));
module.exports = router;
in my users.js I have this - working version
var express = require('express');
var usersRouter = express.Router();
var sqllite3 = require('sqlite3').verbose();
usersRouter.get('/login', function(req, res, next) {
res.render('music/login', { title: 'Express' });
});
module.exports = usersRouter;
But I would like to encapsulate the routes I am defining into another function like this not working this just hangs the page.
Modified version of my users.js not working
var express = require('express');
var usersRouter = express.Router();
var sqllite3 = require('sqlite3').verbose();
var router = function () {
usersRouter.get('/login', function (req, res, next) {
res.render('music/login', {title: 'Express'});
});
return usersRouter;
}
module.exports = router;
In the console I can see it comes in tries the get and nevers gets routed I see this "GET /musicplayer/users/login - - ms - -".
I have even put a console.log right before the return in the anonymous function I created to know it is getting in there and that I am hooking the pathways up right from the parent routes. And I do hit that log action to the screen.
Any help or tips would be appreciated:)
PS in case you are wondering I am trying to separate out apps for different development work I want to play with. So that is why I am doing the sub routing with musicplayer/index.js instead of just putting everything in the app.js for declaring of my main routes.
Router.use() expects an instance of another Router. However your (non-working) module only returns a function.
Use this in your index.js to fix the issue:
router.use('/users', require('./users')());
I'm making a nodeJS module, and I want to use expressJS as a framework for it.
I'm trying to see, how I could go by, including a function inside and app.get(); and call it via another file, such as the actual app.
var express = require("express");
var app = express();
app.get("/", function (req, res) {
exports.type = function (text) {
console.log(req.ip);
console.log(text);
}
});
now when I use this, and i call it on the actual app like:
var web = require("directory_to_file");
var express = require("express");
var app = express();
var http = require("http").Server(app);
app.get("/", function (req, res) {
web.type("Hello, world");
});
http.listen(10022, function () {
console.log("server is up");
});
I get an error:
TypeError: Property 'type' of object #<Object> is not a function
anyone know a way to make it so I can call the function?
There are generally two things you want to export as a module - an API and a Middleware. The classic example of middleware is an authentication module. To do the middleware, just export the middleware. I tend to do a little more than that so I can configure the middleware later. Something along the lines of this:
module.exports = exports = function(config) {
// Do something with config here
return function(req, res, next) {
// your middleware here
};
};
You can then use your middleware in your main program like this:
var app = require('express')(),
mymodule = require('./mymodule');
var config = {}; // replace with whatever config you need
app.use(mymodule(config));
app.listen(process.env.PORT || 3000);
To implement an API, you will create a Router object, then attach your routes to the Router object. You can then "mount" your router in your main program. For example, you could have a file called 'myroutes.js' with the following contents:
var express = require('express'),
myroutes = express.Router();
myroutes.get('/foo', (req, res) => {
res.status(200).type('application/json').send({ myparam: 'foo' });
});
module.exports = exports = myroutes;
Have the following in your main program:
var app = require('express')(),
myroutes = require('./myroutes');
app.use('/api', require('./myroutes'));
app.listen(process.env.PORT || 3000);
Here, in 'myroutes.js', I'm defining a sub-route of /foo and then in the main program, I'm mounting that on /api - so I would access /api/foo to access that API.
In your directory_to_file you are only exporting on app.get('/') which will never be called.
You could add in your directory_to_file the following code
var express = require('express');
var router = express.Router();
router.get('/', function(req, server) {
console.log(req.ip);
});
module.exports = router;
And in your main file you could use app.use('/', web)
A short explanation:
You are creating a new express app / config in your directory_to_file file which won't be launched or used. So your app.get event won't be fired once.
That's why web.type is not a function. You are not exporting anything.
Use the way I provided. This is a commonly used method.
You could call the code I provided a "route". Create multiple routes / route files and include them in your main method.
Your code just looks confused. If I understand you correctly, what you are really trying to do (at least in Node/express terminology) is write your own middleware.
Express is designed with this in mind and it's pretty straightforward e.g.
ipLogger.js
module.exports = function(req, res, next) {
console.log(req.ip);
next();
}
app.js
var http = require("http")
, express = require("express");
, app = express()
, server = http.Server(app)
, ipLogger = require("./ipLogger.js");
app.use(ipLogger()); // log IP of all requests
// handle routes
server.listen(10022, function() {
console.log("server is up");
});
I'm using Express 4.2.0
Is it possible to include a module only once in app.js and use it in any defined route?
Right now this won't work:
app.js
//..
var request = require('request');
var routes = require('./routes/index');
var users = require('./routes/users');
app.use('/', routes);
app.use('/users', users);
//...
routes/user.js
var express = require('express');
var router = express.Router();
router.get('/add', function(req, res) {
var session = req.session;
request('http://localhost:8181/Test?val1=getDepartments', function (error, response, body) {
//...
});
res.render('users/add');
});
module.exports = router;
It will say that "request" is not defined in routes/user.js
ReferenceError: request is not defined
at Object.module.exports [as handle] (C:\inetpub\wwwroot\node7\routes\users.
js:12:5)
Having to include modules in every route which wants to use them doesn't sound like a proper solution...
Yes, there are two ways of creating global variables in Node.js, one using the global object, and the other using module.exports
Here is how,
Method 1. Declare variable without var keyword. Just like importModName = require('modxyz') and it will be stored in global object so you can use it anywhere like global.importModName
Method 2. Using exports option.
var importModName = require('modxyz');
module.exports = importModName ; and you can use it in other modules.
Look here for somemore explanation http://www.hacksparrow.com/global-variables-in-node-js.html
You can leave out the var and do just request = require('request'); but this is frowned upon.
Node.js caches modules. The standard approach is to require modules where needed.