How to create extendable controllers in ExpressJS - javascript

I'm new to Node and I'm trying to create an MVC app with ExpressJS (http://expressjs.com/). I'm using the same folder structure as the MVC example (https://github.com/visionmedia/express/tree/master/examples/mvc) on GitHub.
In my controllers folder, I have 2 folders: main and system. What I'd like is to have a base controller defined in /controllers/system/index.js and have /controllers/main/index.js inherit the system controller. Every other module will extend system and override a few functions to generate a page.
In another tutorial I found the following code.
Base.js
var _ = require("underscore");
module.exports = {
name: "base",
extend: function(child) {
return _.extend({}, this, child);
},
run: function(req, res, next) {
}
};
Home.js
var BaseController = require("./Base"),
View = require("../views/Base"),
model = new (require("../models/ContentModel"));
module.exports = BaseController.extend({
name: "Home",
content: null,
run: function(req, res, next) {
model.setDB(req.db);
var self = this;
this.getContent(function() {
var v = new View(res, 'home');
v.render(self.content);
})
},
getContent: function(callback) {
var self = this;
this.content = {};
model.getlist(function(err, records) {
if(records.length > 0) {
self.content.bannerTitle = records[0].title;
self.content.bannerText = records[0].text;
}
model.getlist(function(err, records) {
var blogArticles = '';
if(records.length > 0) {
var to = records.length < 5 ? records.length : 4;
for(var i=0; i<to; i++) {
var record = records[i];
blogArticles += '\
<div class="item">\
<img src="' + record.picture + '" alt="" />\
' + record.title + '\
</div>\
';
}
}
self.content.blogArticles = blogArticles;
callback();
}, { type: 'blog' });
}, { type: 'home' });
}
});
How do you do this without Underscore's extend function? Does Express have a built in method to extend modules? I'm using doT.js for templating so I don't want to include another large library for one function.
Thanks!
Edit: Had to make a few changes to get the Base code from dc5 to work. System works but for main I'm getting this error on the inherits call:
util.js:555
ctor.prototype = Object.create(superCtor.prototype, {
^
TypeError: Object prototype may only be an Object or null
at Function.create (native)
at Object.exports.inherits (util.js:555:27)
/controllers/system/index.js:
var util = require( 'util' ),
system = { };
system.index = function( req, res, next ) {
res.render( 'main' );
};
module.exports = system;
/controllers/main/index.js:
var util = require( 'util' ),
system = require( './../system/index' ),
main = { };
util.inherits( main, system );
module.exports = main;

You can use util.inherits for what you've described. It isn't an replacement for _.extend(), but all that is needed for the example above is straight inheritance.
Usage: util.inherits(constructor, superConstructor)
base.js:
var util = require('util');
function Base() {…}
Base.prototype.run = function(req,res,next) {…}
module.exports = Base;
home.js:
var util = require('util');
var BaseController = require("./base");
function Home() {…}
util.inherits(home, BaseController);
Home.prototype.run = function(req,res,next) {…}
Or a standard JS inheritance pattern:
base.js:
function Base() {…}
Base.prototype.run = function(req,res,next) {…}
module.exports = Base;
home.js:
var BaseController = require("./base");
function Home() {
BaseController.apply(this, arguments);
}
Home.prototype = Object.create(BaseController.prototype);
Home.prototype.run = function(req,res,next) {…}
From the updated samples in the question, the modules should look like this:
System:
var util = require('util');
function System() {
this.txt = "hello from ";
this.name = "System";
}
System.prototype.sayHello = function() {
console.log(this.txt + this.name);
}
System.prototype.run = function(req,res,next) {
this.sayHello();
console.log('calling ==> overrideMe');
this.overrideMe();
console.log('calling ==> noOverride');
this.noOverride();
next ? next() : "";
}
System.prototype.overrideMe = function() {
console.log('System.overrideMe');
}
System.prototype.noOverride = function() {
console.log('System.noOverride');
}
module.exports = System;
Main:
var util = require('util');
var System = require("../system/");
function Main() {
// Makes sure the System constructor is run
System.apply(this, arguments);
this.name = "Main";
}
util.inherits(Main, System);
Main.prototype.run = function(req,res,next) {
this.sayHello();
console.log('calling ==> overrideMe');
this.overrideMe();
console.log('calling ==> noOverride');
this.noOverride();
next ? next() : "";
}
Main.prototype.overrideMe = function() {
console.log('Main.overrideMe');
}
module.exports = Main;
app.js in the root - a simplified express server:
var System = require('./controllers/system');
var Main = require('./controllers/main');
var express = require('express');
var path = require('path');
var http = require('http');
var app = express();
var server;
var system = new System();
var main = new Main();
app.configure(function() {
"use strict";
app.set('port', process.env.PORT || 3000, '127.0.0.1');
app.use(system.run.bind(system));
app.use(main.run.bind(main));
app.use(app.router);
//app.use(express.compress());
app.use(express.static(path.join(__dirname, '..', 'public'), {redirect: false}));
app.use(express.static(path.join("/Users/dave/personal/playground/yo"), {redirect: false}));
});
server = http.createServer(app);
server.listen(app.get('port'), function() {
"use strict";
console.log("Express server listening on port " + app.get('port'));
});
From the browser access: http://localhost:3000/
Console output:
Express server listening on port 3000
hello from System
calling ==> overrideMe
System.overrideMe
calling ==> noOverride
System.noOverride
hello from Main
calling ==> overrideMe
Main.overrideMe
calling ==> noOverride
System.noOverride
Important
Because this example is using instances of System and Main to provide routes, the run method must be bound to the instance when passing it to express. See: MDN .bind documentation for more information.

To avoid complicated configuration and workarounds, use ZinkyJS, it gives a better approach to make modules inheritance possible.

Related

"Home" is not a Constructor on Node JS

I'm using consign to autoload my applications models, routes and etc... I Have a Model that was Modeling in ES6 style and when I instanciate her it Throws me this error TypeError: app.src.models.Home is not a constructor
I've tried to use ES5 style but no difference. The Object that the consign create is with the models Inside but I cannot access then.
That's my class:
/*function Home() {
}
Home.prototype.getHomeData = function (conexao, callback) {
conexao.query('select * from tarefas', callback)
} //IT does not work like the item below, so I will keep using ES6
*/
class Home {
constructor() {
console.log('Construi')
}
getHomeData(conexao, callback) {
conexao.query('select * from tarefas', callback)
}
}
module.exports = function () {
return Home
}
Look:
Server.js:
var express = require('express'),
bodyparser = require('body-parser'),
app = express();
var consign = require('consign');
consign()
.include('./src/routes')
.then('./src/config/db.js')
.then('./src/models')
.into(app);
console.log("I'm on server.js: ", app.src.models)
app.use(bodyparser.urlencoded({ extended: true }));
app.listen(3000, function () {
console.log("Servidor ON");
});
module.exports = app;
and the console returns properly I'm on server.js: { 'home.model': [Function: Home] }
When I get from the Route It keeps showing me that the app.src.modelshas data,
Console output: I'm on the home.js and still have data + { 'home.model': [Function: Home] }
But When I try to Instanciate the class I throws the error cited above...
module.exports = function (app) {
app.get('/', function (req, res) {
console.log("I'm on the home.js and still have data +", app.src.models)
var conexao = app.src.config.db()
var homeData = new app.src.models.Home();
homeData.getHomeData(conexao, (err, result) => {
if (err) {
res.json(err)
} else {
res.json(result)
}
})
});
}
if I try the below the console gets undefined:
console.log("I'm on the home.js and still have data +", app.src.models.Home)
Here is my repo, if u want https://github.com/tiagosilveiraa/PM_API
Tentative 1:
On class Home i made module.exports = new Home() and it throws the same error
Export your Home class as such:
module.exports = { Home };
And import/use it as such:
const { Home } = require('path/to/Home.js');
const home = new Home();
home.getHomeData(...)

Module sharing and dynamic properties on Express API

I am new to NodeJS / Express and I have this issue related to global/module sharing of modules and configuration settings that I will try to explain here.
A simple example is trying to unit test a middleware module that includes other libraries. As follows:
config/settings.js
module.exports = {
...,
debugLevel: 3
};
middleware/auth.js
// Required modules for middleware
// ----------------------------------------------------------------------
var config = require('../config/settings');
var log = require('../libraries/logger')('middlewares/auth');
...
// Middleware methods
// ----------------------------------------------------------------------
var middleware = {};
middleware.apicall = function(req, res, next) {
...
log.info('Made api call');
};
libraries/logger.js
// External Dependencies
var winston = require('winston');
var winlogger = new (winston.Logger)({
transports: [
new (winston.transports.Console)()
]
});
winlogger.cli();
var logger = function(module, debug) {
this.module = module;
this.logger = winlogger;
this.debugLevel = debugLevel;
};
logger.prototype = {
module: '',
logger: null,
debugLevel: 3,
/**
* Generic Log function
*
* #param level
* #param str
* #param data
*/
debug: function(level, str, data) {
if ( this.debugLevel > 2) this.logger.log(level, this.module+' - '+str, data);
},
/**
* Specific function for info
*
* #param str
* #param data
*/
info: function(str, data) {
if ( this.debugLevel > 1 ) this.logger.info(this.module+' - '+str, data);
}
};
tests/api.js
const chai = require('chai');
const should = chai.should;
const expect = chai.expect;
const assert = chai.assert;
// Node Http Mocks
var httpMocks = require('node-mocks-http');
var middleware = require('../middlewares/auth');
describe("Test API Call", function() {
it("should give 200 over api call", function(done) {
var req = httpMocks.createRequest({
method: 'GET',
url: '/test',
params: {}
});
var res = httpMocks.createResponse();
middleware.apicall(req, res, function(res) { return res; });
assert.equal(res.statusCode, 200);
done();
});
});
Now all this works but the problem i am having is: how can I do the tests without the log line from apicall method showing up ?
Is there a way for the settings to be globally present ? Or a way to structure the app so that you can inject the settings coming from one single require in app.js ?
I would need to do, in tests/api.js, something like this:
var config = require('../config/settings');
config.debug = 0;
and inject it into the test probably. Anyone faced a similar challenge ?
ANSWER
Having not found a solution, I tried to make a series of tests and came up with a solution that works.
We make use of globals for this particular case. So:
bin\www
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
...
var http = require('http');
global.config = require('../config/settings');
global.log = require('../libraries/logger')('server');
...
app.js
// Node framework dependencies
// ----------------------------------------------------------------------
var express = require('express');
var config = global.config;
var path = require('path');
var log = global.log;
log.setLabel('app');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var cors = require('cors');
var validator = require('express-validator');
...
tests\api.js
const chai = require('chai');
const should = chai.should;
const expect = chai.expect;
const assert = chai.assert;
global.config = require('../config/settings');
config.debug = false;
global.log = require('../libraries/logger')('Test case');
...
// Node Http Mocks
var httpMocks = require('node-mocks-http');
var middleware = require('../middlewares/auth');
describe("Test API Call", function() {
it("should give 200 over api call", function(done) {
var req = httpMocks.createRequest({
method: 'GET',
url: '/test',
params: {}
});
var res = httpMocks.createResponse();
middleware.apicall(req, res, function(res) { return res; });
assert.equal(res.statusCode, 200);
done();
});
});

Variable does not return desired object

I am building an Express app. I currently have an error in the following file, factoryRepository.js
let appReference = null;
module.exports.init = (app) => {
appReference = app;
};
module.exports.getRepositoryFactory = () => {
let repositoryFactory = {
getUserRepository: () => {
return require("./UserRepository").init(appReference.get('models').User);
}
};
return repositoryFactory;
};
appReference.get throws a TypeError because appReference is still null even after I have called module.exports.init somewhere else.
I have tried to make a function that returns appReference so I can see in what state it is. I have been able to get app rather than null, it is only in the context of getUserRepository that it stays null.
The faulty line is only called when I ping a certain route.
EDIT:
This is app.js, the context from which module.exports.init is being called
import express from 'express';
import passport from 'passport';
import config from './config/config';
let app = express();
// Setup models
app.set('models', require('./app/models'));
require('./app/repo/repositoryFactory').init(app);
// Setup config
require('./config/init')(app);
// Setup routes
require('./app/routes')(app, passport);
// Setup passport
require('./app/auth')(passport, config);
// Route to ends
require('./config/endpoints')(app);
export default app;
public.js is the logic given to my router, and it is here where I call the faulty code with repositoryFactory.getUserRepository()
let repositoryFactory = require('../repo/RepositoryFactory').getRepositoryFactory();
module.exports.doLogin = (req, res) => {
let success = () => {
res.redirect('/');
};
let error = (message) => {
res.status(500).json(message);
};
let userRepository = repositoryFactory.getUserRepository();
userRepository.findOrCreate({
facebookId: req.user.id,
displayName: req.user.displayName
}, success, error);
};
module.exports.doLogout = (req, res) => {
req.logout();
res.json({
success: true,
message: 'You\'ve succesfully logged out.'
});
};
This looks like a scope issue. Let's say we simplify your three files as follows, and also put them all in the same directory (for simplicity here):
app.js:
'use strict';
const express = require('express');
let app = express();
require('./repositoryFactory').init(app);
const myPublic = require('./public');
repositoryFactory.js:
'use strict';
let appReference = null;
module.exports.init = (app) => {
appReference = app;
};
module.exports.getRepositoryFactory = () => {
return appReference ? 'I have an app reference!' : 'No app reference!';
};
public.js:
'use strict';
let repositoryFactory = require('./RepositoryFactory').getRepositoryFactory();
console.log(repositoryFactory);
This is going to log No app reference! because the repositoryFactory is not a singleton. The repositoryFactory instance in app.js is a different instance than the repositoryFactory instance in public.js.
One solution would be to pass a parameter for the repositoryFactory instance. repositoryFactory.js would be unchanged, but app.js might look like this:
'use strict';
const express = require('express');
let app = express();
const repositoryFactory = require('./repositoryFactory')
repositoryFactory.init(app);
const myPublic = require('./public');
myPublic.init(repositoryFactory);
myPublic.log();
And the corresponding public.js might look like this:
'use strict';
let repositoryFactory = null;
module.exports.init = (myRepositoryFactory) => {
repositoryFactory = myRepositoryFactory.getRepositoryFactory();
}
module.exports.log = () => {
console.log(repositoryFactory);
}

Embed a property or method on the app object inside of controller

I´m trying associate a method or a property on a app object in kraken.js application, like follows:
controllers/index.js
'use strict';
var IndexModel = require('../models/index');
module.exports = function (app) {
var model = new IndexModel();
app.get('/', function (req, res) {
console.log(app.adventurer);
/* Console should be showing "Bilbo Bagins", but I'm getting 'undefined'.
* See the next source file. */
res.render('index', model);
});
};
/index.js
var kraken = require('kraken-js'),
app = {
adventurer: 'Bilbo Bagins'
};
app.configure = function configure(nconf, next) {
// Async method run on startup.
next(null);
};
app.requestStart = function requestStart(server) {
// Run before most express middleware has been registered.
};
app.requestBeforeRoute = function requestBeforeRoute(server) {
// Run before any routes have been added.
};
app.requestAfterRoute = function requestAfterRoute(server) {
// Run after all routes have been added.
};
if (require.main === module) {
kraken.create(app).listen(function (err) {
if (err) {
console.error(err.stack);
}
});
}
module.exports = app;
Also, i´ve tried publish the property on /config/app.json
Any Thoughts?
Simply add the following key to .config/app.json or make a new .config/app-development.json:
"adventurer": "bilbo"
app.json will look like this:
{
//blah
//blah
"adventurer": "bilbo"
}
and then in ./index.js do this in configure:
app.configure = function configure(nconf, next) {
// Async method run on startup.
next(null);
console.log('my traveler is: ', nconf.get('adventurer'));
};
In response to your comment, if you want to get an app config from the ./controllers/index.js then require the nconf lib and use nconf.get like so:
'use strict';
var nconf = require('nconf');
var IndexModel = require('../models/index');
module.exports = function (app) {
var model = new IndexModel();
//or attach it directly to the app object like so
app.set('adventurer', nconf.get('adventurer'));
console.log('adventurer directly set on app object', app.get('adventurer'));
console.log('controller with app adventurer:', nconf.get('adventurer'));
app.get('/', function (req, res) {
res.render('index', model);
});
};
Fire it up with npm start and watch the console. Peace!

flatiron.js routing and templating with union, director and plates?

Coming from express.js, I want to give flatiron a try for a small project. However, there are some small problems which keep me from actually getting somewhere.
var flatiron = require('flatiron')
, session = require('connect').session
, ecstatic = require('ecstatic')
, path = require('path')
, fs = require('fs')
, plates = require('plates')
, director = require('director')
, winston = require('winston')
, union = require('union');
var router = new director.http.Router();
var server = union.createServer({
before: [
ecstatic(__dirname + '/public')
]
});
router.get('/', function () {
var self = this;
fs.readFile('public/layout.html', 'utf-8', function(err, html) {
[...]
})
});
server.listen(3000, function () {
console.log('Application is now started on port 3000');
});
How does routing with director work? When I leave ecstatic out, I can define routes like '/' and it works, but then I don't get static CSS and JS content. With ecstatic / is replaced with 'index.html' and ecstatic has priority over all defined routes.
- It's the same behavior with connect-static. Route (/) is replaced by index.html.
I also tried a different approach using the connect middleware, which doesn't work:
var flatiron = require('flatiron')
, connect = require('connect')
, path = require('path')
, fs = require('fs')
, plates = require('plates')
, app = flatiron.app;
app.use(flatiron.plugins.http);
app.use(connect.favicon());
app.use(connect.static(__dirname + '/public'));
app.use(connect.directory(__dirname + '/public'));
app.use(connect.cookieParser('my secret here'));
app.use(connect.session({'secret': 'keyboard cat'}));
app.router.get('/', function () {
console.log("GET /");
var self = this;
fs.readFile('public/layout.html', 'utf-8', function(err, html) {
[...]
})
});
app.listen(3000, function () {
console.log('Application is now started on port 3000');
});
I think the best answer for your question about routing in flatiron is, as always, inside the source code:
app.server = union.createServer({
after: app.http.after,
before: app.http.before.concat(function (req, res) {
if (!app.router.dispatch(req, res, app.http.onError || union.errorHandler)) {
if (!app.http.onError) res.emit('next');
}
}),
headers: app.http.headers,
limit: app.http.limit
});
As you can see here flatiron binds router as the last request handler, that is called after all middleware. If you place 'ecstatic' in app.http.before and it will be dispatched during workflow, no other middleware would be called.
Your second block of code demonstrates that you don't undestand difference between Flatiron's .use() method from Express/Connect's. I'll try to make it clear on this example:
flatironApp.use({
// plugin object
name : "pluginName"
, attach : function(options) {
/*code*/
}
, init : function(done) {
/*code*/
done();
}
})
connectApp.use(function(req, res, next) {
/* code */
next();
})
If you want to use Connect's middleware in Flatiron you should place it respectively in app.http.before array like this:
// Initiating application
app.use(flatiron.plugins.http);
// Adding request handlers
app.http.before.push( connect.favicon() );
app.http.before.push( ecstatic(__dirname + '/public') );
var connect = require('connect');
var server = union.createServer({
before: [
function (req, res) {
var found = router.dispatch(req, res);
if (!found) {
res.emit('next');
}
},
connect.static('public')
]
});
I forgot to insert the dispatch-function. This works.

Categories