I am a newbie to Node.JS and began looking for best practice articles, etc. to make sure my node.js code was unit test friendly and followed the node.js best practices. While researching I came across the following code examples:
app.js:
module.exports = function (flights) {
var express = require('express');
var routes = require('./routes')(flights);
var path = require('path');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(function (req, res, next) {
res.set('X-Powered-By', 'Flight Tracker');
next();
});
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/flight/:number', routes.flight);
app.put('/flight/:number/arrived', routes.arrived);
app.get('/list', routes.list);
return app;
}
server.js:
var http = require('http'),
flights = require('./data'),
app = require('./app')(flights);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
module(index.js):
var Flight = function () {
this.data = {
number: null,
origin: null,
destination: null,
departs: null,
arrives: null,
actualDepart: null,
actualArrive: null
};
this.fill = function (info) {
for(var prop in this.data) {
if(this.data[prop] !== 'undefined') {
this.data[prop] = info[prop];
}
}
};
this.triggerDepart = function () {
this.data.actualDepart = Date.now();
};
this.triggerArrive = function () {
this.data.actualArrive = Date.now();
};
this.getInformation = function () {
return this.data;
};
};
module.exports = function (info) {
var instance = new Flight();
instance.fill(info);
return instance;
};
routes(index.js):
module.exports = function (flights) {
var flight = require('../flight');
for(var number in flights) {
flights[number] = flight(flights[number]);
}
var functions = {};
functions.flight = function(req, res){
var number = req.param('number');
if (typeof flights[number] === 'undefined') {
res.status(404).json({status: 'error'});
} else {
res.json(flights[number].getInformation());
}
};
functions.arrived = function (req, res) {
var number = req.param('number');
if (typeof flights[number] === 'undefined') {
res.status(404).json({status: 'error'});
} else {
flights[number].triggerArrive();
res.json({status: 'done'});
}
};
functions.list = function (req, res) {
res.render('list', {
title: 'All Flights',
flights: flights});
};
return functions;
};
While I like how this is organized alot, I do not see an efficient way to handle multiple modules in this scenario. Let's say I have additional modules for User, Initerary, etc. This seems like it could become chaotic quickly as I began to add routes to the app.js file, not to mention how many potential parameters would need to be passed into app.js. What if I have a large rest layer that required 20 or more modules? I am looking for any doc or links or examples demonstrating a clean way I should do this in node.js.
Granularize and encapsulate using sub routers with express.Router(). Each router then becomes it's own module that you pull in making it so that you don't have to add every single route explicitly in app.js.
User.js
"use strict";
// Create a router
var express = require("express"),
router = express.Router();
router.get("/", function (req, res, next) {
// All your User logic
res.send([
{
name: "Bob"
},
{
name: "John"
}
]);
});
router.get("/:number", function (req, res, next) {
res.send({
name: "Bob"
});
});
router.post("/", function (req, res, next) {
res.send({
name: "New Guy"
});
});
module.exports = router;
App.js
var app = express();
app.use("/user", require("./User"));
Now you can GET /user, GET /user/SOME_ID, and POST to /user. It's also simple if you want to move the sub route around to somewhere else because the methods defined for the user route are relative.
I too have a large enterprise app called campaign_service, consisting of many many modules.
This is how, I organize the routes in a file campaign_router.js.
var express = require('express');
var router = express.Router();
var cm = require('./campaign_manager.js');
var qsc = require('./converters/qs_converter.js');
var jsc = require('./converters/json_converter.js');
var xmlc = require('./converters/xml_converter.js');
var fc = require('./converters/f_converter');
var fc2 = require('./converters/fjt2_converter');
var cv = require('./campaign_validator.js');
var templates = require('./template_list.js');
var AuthDecoder = require('authdecoder');
var adc = AuthDecoder.middleware;
router.get ('/campaigns/qs', qsc, adc, cv, cm.createCampaign); // Creates a new campaign using query string params
router.post ('/campaigns/b/xml', xmlc, adc, cv, cm.createCampaign); // Creates a new campaign using xml payload
router.post ('/campaigns/file', fc, adc, cv, cm.createCampaign); // Creates a new campaign using uploaded file
router.post ('/campaigns/file/type2', fc2, adc, cv, cm.createCampaign); // Creates a new campaign using uploaded file
router.post ('/campaigns/js', jsc, adc,cv, cm.createCampaign); // Creates a new bulk campaign using json payload
router.get ('/campaigns/', adc, cm.getCampaigns); // Returns a list of bulk campaigns accessible for the invoking user.
router.get ('/campaigns/date', adc, cm.getCampaignsByRange); // Returns a list of bulk campaigns accessible for the invoking user.
router.get ('/campaigns/b/:id', adc, cm.getCampaign); // returns the details of a specific campoaign
router.put ('/campaigns/b/id', cm.updateCampaign); // Updates a specified campaign
router.delete ('/campaigns/b/:id', cm.deleteCampaign); // deletes a campaign
router.post ('/pause', adc, cm.pauseJob); // Pauses an ongoing campaign
router.post ('/resume', adc, cm.resumeJob); // Resumes a paused campaign
router.post ('/cancel', adc,cm.cancelJob); // Resumes a paused campaign
//router.post ('/reschedule', adc,cm.rescheduleCampaign); // Resumes a paused campaign
//router.get ('/', cm.pingCampaignServer);
router.get ('/templates', adc, templates.listTemplates); // gives the campaign server templates
Salient points:
All campaign functions, go into another js file (which is a middleware), called campaign_manager.js
All payload converters go into a sub-folder called "converters", and this folder has many files called 'qs_converter.js', 'json_converter.js', ..., blah blah.
My app performs authentication, before any campaign request is accepted, so there is a middleware loaded into the route.
If I have other mount points apart from "/campaigns", I would create another router module and define additional .js files.
_ I would use index.js (or app.js), to load these routers.
Essentially, it is very easy for me to scale my code into multiple modules and import them via the router file.
EDIT
Added usage scenario
Index.js
var routes = require('./campaign_router.js');
...
app.use('/campaignservice', routes);
Related
This question already has answers here:
How to separate routes on Node.js and Express 4?
(9 answers)
Closed 1 year ago.
In my NodeJS express application I have app.js that has a few common routes. Then in a wf.js file I would like to define a few more routes.
How can I get app.js to recognize other route handlers defined in wf.js file?
A simple require does not seem to work.
If you want to put the routes in a separate file, for example routes.js, you can create the routes.js file in this way:
module.exports = function(app){
app.get('/login', function(req, res){
res.render('login', {
title: 'Express Login'
});
});
//other routes..
}
And then you can require it from app.js passing the app object in this way:
require('./routes')(app);
Have a look at these examples: https://github.com/visionmedia/express/tree/master/examples/route-separation
In Express 4.x you can get an instance of the router object and import another file that contains more routes. You can even do this recursively so your routes import other routes allowing you to create easy-to-maintain URL paths.
For example, if I have a separate route file for my /tests endpoint already and want to add a new set of routes for /tests/automated I may want to break these /automated routes out into a another file to keep my /test file small and easy to manage. It also lets you logically group routes together by URL path which can be really convenient.
Contents of ./app.js:
var express = require('express'),
app = express();
var testRoutes = require('./routes/tests');
// Import my test routes into the path '/test'
app.use('/tests', testRoutes);
Contents of ./routes/tests.js:
var express = require('express'),
router = express.Router();
var automatedRoutes = require('./testRoutes/automated');
router
// Add a binding to handle '/tests'
.get('/', function(){
// render the /tests view
})
// Import my automated routes into the path '/tests/automated'
// This works because we're already within the '/tests' route
// so we're simply appending more routes to the '/tests' endpoint
.use('/automated', automatedRoutes);
module.exports = router;
Contents of ./routes/testRoutes/automated.js:
var express = require('express'),
router = express.Router();
router
// Add a binding for '/tests/automated/'
.get('/', function(){
// render the /tests/automated view
})
module.exports = router;
Building on #ShadowCloud 's example I was able to dynamically include all routes in a sub directory.
routes/index.js
var fs = require('fs');
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
Then placing route files in the routes directory like so:
routes/test1.js
module.exports = function(app){
app.get('/test1/', function(req, res){
//...
});
//other routes..
}
Repeating that for as many times as I needed and then finally in app.js placing
require('./routes')(app);
If you're using express-4.x with TypeScript and ES6, this would be the best template to use:
src/api/login.ts
import express, { Router, Request, Response } from "express";
const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
try {
res.send('OK');
} catch (e) {
res.status(500).send(e.toString());
}
});
export default router;
src/app.ts
import express, { Request, Response } from "express";
import compression from "compression"; // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';
const app = express();
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.get('/public/hc', (req: Request, res: Response) => {
res.send('OK');
});
app.use('/user', login);
app.listen(8080, () => {
console.log("Press CTRL-C to stop\n");
});
Much cleaner than using var and module.exports.
Full recursive routing of all .js files inside /routes folder, put this in app.js.
// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');
function recursiveRoutes(folderName) {
fs.readdirSync(folderName).forEach(function(file) {
var fullName = path.join(folderName, file);
var stat = fs.lstatSync(fullName);
if (stat.isDirectory()) {
recursiveRoutes(fullName);
} else if (file.toLowerCase().indexOf('.js')) {
require('./' + fullName)(app);
console.log("require('" + fullName + "')");
}
});
}
recursiveRoutes('routes'); // Initialize it
in /routes you put whatevername.js and initialize your routes like this:
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('index', { title: 'index' });
});
app.get('/contactus', function(req, res) {
res.render('contactus', { title: 'contactus' });
});
}
And build yet more on the previous answer, this version of routes/index.js will ignore any files not ending in .js (and itself)
var fs = require('fs');
module.exports = function(app) {
fs.readdirSync(__dirname).forEach(function(file) {
if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
I am trying to update this answer with "express": "^4.16.3". This answer is similar to the one from ShortRound1911.
server.js:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;
const app = new express();
//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));
//...fire connection
mongoose.connect(db.url, (err, database) => {
if (err) return console.log(err);
//...fire the routes
app.use('/', routes);
app.listen(port, () => {
console.log('we are live on ' + port);
});
});
/src/routes/index.js:
const express = require('express');
const app = express();
const siswaRoute = require('./siswa_route');
app.get('/', (req, res) => {
res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);
module.exports = app;
/src/routes/siswa_route.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({item: 'Siswa page...'});
});
module.exports = app;
If you want a separate .js file to better organize your routes, just create a variable in the app.js file pointing to its location in the filesystem:
var wf = require(./routes/wf);
then,
app.get('/wf', wf.foo );
where .foo is some function declared in your wf.js file. e.g
// wf.js file
exports.foo = function(req,res){
console.log(` request object is ${req}, response object is ${res} `);
}
One tweak to all of these answers:
var routes = fs.readdirSync('routes')
.filter(function(v){
return (/.js$/).test(v);
});
Just use a regex to filter via testing each file in the array. It is not recursive, but it will filter out folders that don't end in .js
I know this is an old question, but I was trying to figure out something like for myself and this is the place I ended up on, so I wanted to put my solution to a similar problem in case someone else has the same issues I'm having. There's a nice node module out there called consign that does a lot of the file system stuff that is seen here for you (ie - no readdirSync stuff). For example:
I have a restful API application I'm trying to build and I want to put all of the requests that go to '/api/*' to be authenticated and I want to store all of my routes that go in api into their own directory (let's just call it 'api'). In the main part of the app:
app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));
Inside of the routes directory, I have a directory called "api" and a file called api.js. In api.js, I simply have:
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;
Everything worked as expected. Hope this helps someone.
index.js
const express = require("express");
const app = express();
const http = require('http');
const server = http.createServer(app).listen(3000);
const router = (global.router = (express.Router()));
app.use('/books', require('./routes/books'))
app.use('/users', require('./routes/users'))
app.use(router);
routes/users.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'John Smith'})
}
module.exports = router
routes/books.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'Dreams from My Father by Barack Obama'})
}
module.exports = router
if you have your server running local (http://localhost:3000) then
// Users
curl --request GET 'localhost:3000/users' => {name: 'John Smith'}
// Books
curl --request GET 'localhost:3000/books' => {name: 'Dreams from My Father by Barack Obama'}
I wrote a small plugin for doing this! got sick of writing the same code over and over.
https://www.npmjs.com/package/js-file-req
Hope it helps.
you can put all route functions in other files(modules) , and link it to the main server file.
in the main express file, add a function that will link the module to the server:
function link_routes(app, route_collection){
route_collection['get'].forEach(route => app.get(route.path, route.func));
route_collection['post'].forEach(route => app.post(route.path, route.func));
route_collection['delete'].forEach(route => app.delete(route.path, route.func));
route_collection['put'].forEach(route => app.put(route.path, route.func));
}
and call that function for each route model:
link_routes(app, require('./login.js'))
in the module files(for example - login.js file), define the functions as usual:
const login_screen = (req, res) => {
res.sendFile(`${__dirname}/pages/login.html`);
};
const forgot_password = (req, res) => {
console.log('we will reset the password here')
}
and export it with the request method as a key and the value is an array of objects, each with path and function keys.
module.exports = {
get: [{path:'/',func:login_screen}, {...} ],
post: [{path:'/login:forgotPassword', func:forgot_password}]
};
I have this AngularJS SPA with ui-router which works perfectly. It uses parse.com as the backend, and I have it working on a regular Apache server.
Now I want to move it to a node.js server-app, and I want node.js to handle all the CRUDs for parse.com.
I set up a nice little node.js app to act as server, and it works.
My question is: How do I handle requests between my node.js server-app and my AngularJS SPA?
I've included my server.js file, in case anyone can use it.
// set up =====================================================================================================
var express = require('express'),
path = require('path'),
morgan = require('morgan'),
bodyParser = require('body-parser'),
methodOverride = require('method-override'),
routes = require('routes'),
keys = require('./config/keys'),
port = 80;
var app = express();
var Parse = require('parse/node').Parse;
// view engine setup ==========================================================================================
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// configuration ==============================================================================================
app.use(require('prerender-node').set('prerenderToken', keys.prerender));
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
app.use(express.static(path.join(__dirname, 'public_html')));
Parse.initialize(keys.app, keys.js);
// routing ====================================================================================================
app.use(function(req, res) {
"use strict";
// use res.sendfile, as it streams instead of reading the file into memory. ===============================
res.sendfile(__dirname + '/public_html/index.html');
});
app.use('/', routes);
// catch 404 and forward to error handler =====================================================================
app.use(function(req, res, next) {
"use strict";
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// listen (start app with node server.js) =====================================================================
app.listen(port);
console.log("App listening on port %d", port);
File structure:
- public_html/ <-- the angularjs app -->
- node_modules/ <-- installed modules for node -->
- config/
- keys.js
- server.js
- package.json
The current setup
So, currently, I would deal with parse.com data in my (angularjs) app controller - and some of it in parse's cloud-code.
Now, I want to move all parse.com dealings to my node.js server-app, so that my angularjs "calls" the node.js server-app, which in turn "calls" parse.com for the data I need, and then return it to the angularjs app so I can update my views (with the new data from parse.com).
Example of what I want
parse.com <--> node.js server-app <--> angularjs SPA (views)
A simple thing I do in my controller is something like
var Profile = Parse.Object.extend('Profile');
var query = new Parse.Query(Profile);
query.equalTo('objectId', $stateParams.authorPermaLink);
query.find().then(function(results){
var object = results[0];
$scope.authorObj = results[0];
$scope.template.pageName = object.get('screenname');
$scope.template.pageAuthor = object.get('screenname');
$scope.template.pagePublished = object.createdAt;
$scope.template.pageLastEdit = object.updatedAt;
$scope.$apply();
}, function(error){
// error-handling
console.log("Error: " + error.code + " " + error.message);
});
Now, moving this snippet of code to node.js is simple because I can use the parse.com SDK in node.js directly. But how do I get the angularjs app to communicate with the node.js server-app?
Okay, so I managed to brain my way out of this.
The solution is to make routes that will handle http-requests from my angularjs app.
routes.js (node):
app.get('/api/article/:permalink', function(req, res) {
blog.getArticle(req.params.permalink, function(data) {
res.json(data);
});
});
// everything else
app.use(function(req, res) {
// use res.sendfile, as it streams instead of reading the file into memory.
res.sendfile('./public_html/index.html');
});
Then create a model with the blog object and methods, which are called above.
model.js (also node):
module.exports = {
getArticle : function(permalink, callback) {
"use strict";
var Article = Parse.Object.extend('Article');
var query = new Parse.Query(Article);
query.include('category');
query.include('profile');
query.equalTo('permalink', permalink);
query.find().then(function(results) {
var result = results[0];
var object = {
title: result.get('title'),
screenname: result.get('profile').get('screenname'),
profileID: result.get('profile').id,
content: result.get('content'),
publishedAt: result.get('publishedAt'),
updatedAt: result.updatedAt,
categoryName: result.get('category').get('categoryName'),
categoryPermaLink: result.get('category').get('categoryPermaLink'),
articleID: result.id
};
callback(object);
}, function(error) {
callback({error: error});
});
}
};
And then finally in mu angularjs app, simply make a http-request (get in this example but the other rest verbs all work as well).
controller.js (angularjs):
$http.get('/api/article/' + $stateParams.articlePermaLink
).then(function successCallback(response) {
response = response.data;
$scope.articleTitle = response.title;
$scope.template.pageName = response.title;
$scope.articleAuthor = response.screenname;
$scope.template.pageAuthor = response.screenname;
$scope.profilePermaLink = response.profileID;
$scope.articleContent = response.content;
$scope.publishDate = response.publishedAt;
$scope.template.pagePublished = response.publishedAt;
$scope.template.pageLastEdit = response.updatedAt;
$scope.category = response.categoryName;
$scope.categoryPermaLink = response.categoryPermaLink;
$scope.currentArticle = response.articleID;
console.log(response.commentObj);
}, function errorCallback(response) {
console.log("Error: " + response.code + " " + response.message);
});
The callbacks are the magic!
I have tried every answer I've found on s/o, and I'm sure I must be missing something. What doesn't error on me instead gives me a 404. I tried answers from Organize routes in Node.js, strongloop's route-separation pattern, the answers from How to include route handlers in multiple files in Express?, hit similar errors as in Router.use requires middleware function? but none of those answers worked, either. The answer for Unable to Split Routes into Separate Files in Express 4.0 doesn't error, but also 404s. It seems like each answer has a different syntax and style, and maybe it's that I'm mixing and matching incorrectly?
Right now my /routes/persons.js has this pattern:
var express = require('express');
var persons = express.Router();
persons.route('/persons/:user_id')
.put(function (req, res, next) {
// etc
});
module.exports = persons;
In my server.js file, I've got:
var persons = require('./routes/persons');
app.use('/persons', persons);
This combination doesn't throw errors, but it also doesn't do anything. I've tried adding the endpoint to server.js lines:
var persons = require('./routes/persons');
app.get('/persons/:user_id', persons.addpersons);
and stripping persons.js down to just export functions:
exports.addpersons = function (req, res, next) {
var list = req.body;
// etc
}
Plus variations like wrapping the whole person.js file in module.exports = function(), sticking module.exports = router at the end, using app instead of router, etc.
What am I overlooking? Should I be adding some other middleware, rearranging how I call the endpoint, using app, or sticking with router.route? What are the most likely culprits when there's no error but the endpoint is still 404'ing?
many thanks in advance!
============= EDITED TO INCLUDE SERVER.JS =============
Since it's clear something is set wrong, somewhere, here's my server.js file:
var express = require('express');
var app = express();
var methodOverride = require('method-override');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var router = express.Router();
var jwt = require('jsonwebtoken');
var config = require('./config');
var nodemailer = require('nodemailer');
var bcrypt = require('bcrypt-nodejs');
var crypto = require('crypto');
var async = require('async');
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'email#gmail.com',
pass: 'password'
}
});
// I don't know if both are necessary, used multiple conflicting tutorials
app.use(require('express-session')({
secret: 'secret',
resave: false,
saveUninitialized: false
}));
app.set('superSecret', config.secret);
var Schema = mongoose.Schema,
Person = require('./models/person.js'),
User = require('./models/user.js'),
Event = require('./models/event.js');
var port = process.env.PORT || 8080;
mongoose.connect(config.database);
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(express.static(__dirname + '/public'));
// routes go here
app.use('/api', router);
app.listen(port);
console.log('gogogo port ' + port);
I have no idea where else I might look for why including routes requires such a break in the usual pattern. My config files? My procfile? Those are the only other files sitting on the server, not counting /models and /routes.
The key here is to understand what app.use() does to your req object (in particular to req.path), how app.get() and friends are different, and how Express wraps path-to-regexp (its internal path matching module) to handle routes.
1) app.use(path, middleware) mounts the middleware. Inside the mounted middleware/router, req.path is relative to the mount path. Only the beginning of the request path needs to match, so /foo will work for requests at /foo (relative path will be /), /foo/bar (relative path is /bar), etc.
app.use(function (req, res, next) {
console.log('Main: %s %s', req.method, req.path);
next();
});
app.use('/foo', function (req, res) {
console.log('In /foo: %s %s', req.method, req.path);
res.send('Got there');
});
Try running the setup above, navigate to localhost/foo and see the following logs:
Main: GET /foo
In /foo: GET /
2) app.get(path, middleware), app.post(path, middleware) etc. do not mount the target middlewares, so req.path is preserved. req.path must match the whole pattern you defined your route with, so /foo will only work for /foo requests.
app.use(function (req, res, next) {
console.log('Main: %s %s', req.method, req.path);
next();
});
app.get('/foo', function (req, res) {
console.log('In /foo: %s %s', req.method, req.path);
res.send('Got there');
});
Navigate to localhost/foo and see :
Main: GET /foo
In /foo: GET /foo
3) app.route(path), as explained in the Express docs, is just a convenience to define multiple app.get(middleware), app.post(middleware) etc. sharing the same path.
Now in your case, here is a working setup:
main
var persons = require('./routes/persons');
app.use('/persons', persons);
routes/persons.js
var router = require('express').Router();
router.route('/:user_id')
.post(function (req, res) {
// handle potato data
})
.get(function (req, res) {
// get and send potato data
});
module.exports = router;
This is convenient as you only have to set the /persons entry point once in your main file, so you can easily update it later on if needed (you could also import that path value from a config file, from your router object or whatever, Node is pretty flexible in this regard). The persons router itself takes care of its business controllers, regardless of where it is exactly mounted at.
I FIGURED IT OUT!
Of course, this might be the totally wrong way to go about it (pls tell me if so) but it WORKS.
in my server.js file, I have:
var persons = require('./routes/persons');
router.get('/persons/:user_id', persons);
router.post('/persons/:user_id', persons);
and my persons.js file now looks like this:
var mongoose = require('mongoose');
var express = require('express');
var router = express.Router();
var Schema = mongoose.Schema,
Person = require('../models/person.js');
router.post('/persons/:user_id', function (req, res) {
var potatoBag = req.body;
Person.collection.insert(potatoBag, function onInsert(err, potatoBag) {
if (err) {
return res.json(err);
} else {
res.status(200).end();
}
});
});
router.get('/persons/:user_id', function(req, res) {
var id = req.params.user_id;
Person.find({'user_id':id},function(err, person) {
if (err)
return res.json(err);
res.send(person);
});
});
module.exports = router;
This seems like more overhead than most of the examples, but maybe it's because of a) using router.route and b) using imported schemas? I also had (req, res, next) in there, and it threw fits until I removed the next pieces. Probably still a bit awkward, but hey, it's working. Thanks for the help, everyone!
instead of
persons.route('/persons/:user_id')
.put(function (req, res, next) {
// etc
});
do:
persons.put('/persons/:user_id',function (req, res, next) {
// etc
});
I have a node.js (with express) module that connects to twitter and streams data based on search terms the user inputs in the front end (see: twitter.tweetStream(api, params, values, track, bridgeArray, usernameArray, scoreData);). The module executes when the user submits a form and is directed to /test, but it continues to run even after they leave /test. It also runs in parallel with any new instance of it that starts.
Is there a way to tell the module to stop running if the user leaves the /test route? Th
// Renders form on default Route
app.get('/', function(req, res){
res.render('form');
});
// On form submission take data and passes it into twitter stream call as "track" object then renders the 'tweets' feed
app.post('/test',function(req,res){
var track = req.body;
twitter.tweetStream(api, params, values, track, bridgeArray, usernameArray, scoreData);
res.render('tweets');
});
// Renders Tweets stored in "values object" onto a page to be called from tweets template
app.get('/tweets', function(req, res){
res.render('home', {
profileImg: values.profileImg,
userName: values.userName,
screenName: values.screenName,
text: values.text,
timeStamp: values.timeStamp,
tweetScore: values.tweetScore,
// totals: values.totals
});
});
Complete code added for clarity:
var express = require('express');
var app = express();
//var http = require('http').Server(app); // Not using
var exphbs = require('express-handlebars');
var bodyParser = require('body-parser');
var twitter = require('./twitter.js');
var hue = require("./hue_control.js");
// Variable that control Twitter Calls
var api = 'statuses/filter';
var params = {slug:'REDACTED', owner_screen_name: 'REDACTED', skip_status: true};
var values = {};
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');
app.use(bodyParser());
app.use(express.static('public'));
// Checks for Hue bridge on local network gets IP and sets usename
var bridgeArray = [];
var usernameArray = [];
var scoreData = [];
var track = {};
hue.activate(bridgeArray, usernameArray);
// Renders form on default Route
app.get('/', function(req, res){
res.render('form');
});
// On form submission take data and passes it into twitter stream call as "track" object then renders the 'tweets' feed
app.post('/tweets',function(req,res){
track = req.body;
res.render('tweets');
twitter.tweetStream(api, params, values, track, bridgeArray, usernameArray, scoreData);
});
// Renders Tweets stored in "values object" onto a page to be called from tweets template
app.get('/tweetstatic', function(req, res){
res.render('home', {
profileImg: values.profileImg,
userName: values.userName,
screenName: values.screenName,
text: values.text,
timeStamp: values.timeStamp,
tweetScore: values.tweetScore,
});
});
app.get('/totals', function(req, res){
res.render('totals', {
tweetTotal: values.tweetTotal,
score: values.score,
color: values.color
});
});
// App Settings
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
I have loads of router.get functions in my code which I think, could be reduced to a single switch-case function. Here is what I have tried:
function handlerA(req, res) {}
function handlerB(req, res) {}
var routes = {
'/url-one': handlerA,
'/url-two': handlerB
}
router.get('/*', function(req, res) {
var url = req.url;
if (routes[url]) {
routes[url](req, res);
}
});
This works but also, significantly slows my application. Is there any other solution which would not hit the performance of my app?
Thanks
Is there a reason you don't want to use router.get functions? I would guess express.js is internally performing the same logic that you are doing anyway. You are just replacing get functions with handlers.
If you are using similar logic between multiple routes, that may be worth abstracting.
I usually go with a setup like this:
app.js
routes.js
api/
user/
index.js
user.controller.js
user.model.js
image/
index.js
image.controller.js
image.model.js
/api/user/index.js:
var express = require('express');
var controller = require('./user.controller');
var router = express.Router();
router.get('/', controller.index);
router.post('/', controller.create);
module.exports = router;
/api/user/user.controller.js:
var User = require('./user.model');
exports.index = function(req, res) {
// Show list of users
};
exports.create = function (req, res, next) {
// Create user
};
/routes.js:
module.exports = function(app) {
// Insert routes below
app.use('/api/users', require('./api/user'));
app.use('/api/images', require('./api/image'));
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
// All other routes should redirect to the index.html
app.route('/*')
.get(function(req, res) {
res.sendfile(app.get('appPath') + '/index.html');
});
};
And lastly, the /app.js:
// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var express = require('express');
var mongoose = require('mongoose');
var config = require('./config/environment');
// Connect to database
mongoose.connect(config.mongo.uri, config.mongo.options);
// Populate DB with sample data
if(config.seedDB) { require('./config/seed'); }
// Setup server
var app = express();
var server = require('http').createServer(app);
require('./config/express')(app);
require('./routes')(app);
// Start server
server.listen(config.port, config.ip, function () {
console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});
// Expose app
exports = module.exports = app;
Most of this is directly from the Yeoman Generator Angular-Fullstack and it has a really nice setup!