Skip to content

Latest commit

 

History

History
283 lines (204 loc) · 7.04 KB

File metadata and controls

283 lines (204 loc) · 7.04 KB

Express.js

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.

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'));
});

nodemon, forever, pm2

▶ npm i -g nodemon

Structuring

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')));
};

Routes

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();

Request and Response

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.params

request is not used for CORS

Blocking

Node.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.

Middleware

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
]));

Controllers

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) {
  }
};

Security

Sample

Deprecation

app.configure() is deprecated in version 4.