Node has a built in http module that allows us to create a server. The problem is that configuration is pretty overwhelming and difficult (memory leaks, etc.). Most people use express.js even there is still a bit of configuration going on, but it has a welcoming API.
- express.js Repo
- Production best practices
- jshttp - Low-level JavaScript HTTP-related Modules
- Performance at rest
Node.js is a single-threaded event loop. It enqueues and dequeues events. That's all.
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.set('port', process.env.PORT || 3000);
// Using middleware
app.use(bodyParser.json());
// Using route
app.get('/', function(req, res) {
});
// Boot up the app
var server = http.createServer(app);
server.listen(app.get('port'), function() {
console.info('Express listening on port ' + app.get('port'));
});▶ npm i -g nodemon
Instead of having Rails-like controllers, models, helpers folder, we can structure our in a service/feature/resource-oriented way where a feature has its own folder to house all controllers, routers, models, etc. This way if a feature is muted, we can delete that feature only instead of looking through several folders. Express's mini-router encourages this setup.
require('./config/routes')(app, express);
// In config/routes
module.exports = function(app, express) {
app.use(express.static(path.join(__dirname, 'public')));
};With Express 4, we can now have more than one router. Think of router as module with its own routing and middleware stack and functionality. The global app will still control global middleware and configuration and setup routing for the other routers we make.
Finally, we can have more than one router.
var app = express();
var candidateRouter = express.Router();
candidateRouter.get('/profile', function(req, res) {
// This will be /ca/profile
});
// Can also use route for many verbs
candidateRouter.route('/profile')
.get(function(req, res) {})
.delete(function(req, res) {});
app.use('/ca', candidateRouter);// At app.js or server/index.js
require('./routes')(app);
// At routes/index.js
var fs = require('fs');
var path = require('path');
module.exports = function(app) {
fs.readdirSync('./routes').forEach(function(file) {
// Avoid current file
if (file === path.basename(__filename)) { return; }
require('./' + file)(app);
});
};
// For any new routes, just add like routes/auth.js
module.exports = function(app) {
app.get('/login', function(req, res) {
res.render('login');
});
};var staffRouter = require('express').Router();
staffRouter.param('id', function(req, res, next, id) {
var staff = _.find(staffs, { id: id });
if (staff) {
req.staff = staff;
next();
} else {
res.send();
}
});
staffRouter.get('/:id', function() {});
staffRouter.put();res.json()
res.send() // also convert to JSON
res.status(500).send(error)
req.body // using Fetch API with JSON.stringify body and express will convert the JSON to JavaScript object
req.paramsNode.js is more I/O bound than CPU bound. Intensive computation like Fibonacci or Prime Number really has no place in a Node.js program.
If you JSON.parse() a 15MB of JSON file it will take about 2 seconds and also block all other requests.
Express uses middleware to modify and inspect incoming request.
Middleware can run any code, change the request and response objects, end the request-response cycle, and call the next middleware in the stack.
Middleware is an amazingly useful pattern that allows developers to reuse code and share it with others via NPM modules.
// Mounting by prefixing the middleware
app.use('/admin', function(req, res, next) {
next();
});There are 3rd party, router-level, application-level, error-handling and built-in middleware.
// Error-handling middleware
app.use(function (err, req, res, next) {});Nice for handling authentication also. Here, we are using middleware on a route.
var checkAuth = require('./util/checkauth');
app.get('/users', [checkAuth()], function(req, res) {
// Auth is good here
});It is sort of like Rails's before_action.
app.all('/ca/*', [isAuthenticated, isCandidate], function(req, res, next) {
});
app.all('*', function(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
next(new Error(401)); // 401 Unauthorized
// Or
res.status(401).send({message: 'access denied'});
}
});router.route('/rooms/edit/:id')
.all(function(req, res, next) {
var roomId = req.params.id;
var room = _.find(rooms, r => r.id === roomId);
res.locals.room = room;
next()
})
.get(function(req, res) {
res.render('edit', { room: res.locals.room })
})
.post(function(req, res) {
})Name your function:
app.use(function forceLiveDomain(req, res, next) {
// ...
return next();
});Performance of middleware should be quick. Do not make too many HTTP request in middleware as it run series from top of the stack to the bottom. Use Async.js for making HTTP requests.
function parallel(middlewares) {
return function(req, res, next) {
async.each(middlewares, function(mw, cb) {
mw(req, res, cb);
}, next);
};
}
app.use(parallel([
getUser,
getSiteList,
getCurrentSite,
getSubscription
]));var controller = require('./controller');
module.exports.initialize = function(app, router) {
router.get('/', controller.index);
router.get('/login', controller.login);
router.post('/login', controller.processLogin);
router.post('/movies', controller.addMovie);
app.use('/', router);
};
// In controller.js
var request = require('request');
module.exports = {
index: function(req, res) {
},
addMovie: function(req, res) {
}
};app.configure() is deprecated in version 4.