Website/Web Service template in Node.js
Table of Contents
I wrote this template to help me create websites and Web Services with Node. It uses vanilla http server with a few small packages that most websites need. Stuff like templates, router and serving static files. I borrowed a lot from isaacs' npm website.
Sites that follow this approach: (pull requests for more examples are welcome)
Why not express
When newcomers ask about writing a website or a Web Service they are usualy being told to use express. The problem with express is it uses middleware/Connect. middleware was a hack invented while solving a problem Node doesn't have. The problem that WSGI solves, which is creating a common interface for writing websites or frameworks that are used by web servers (such as Gunicorn in python or Thin in Ruby). Node doesn't even have those web servers since it comes with a built-in server as part of it's http library.
middleware forces a pre-declared stack of (req,res,next) functions on top of your routes. It's mostly just not a very useful approach for handling the kinds of things you need a webserver to do - serving static files, parsing POST data, parsing cookies, routing, auth. All can be handled quite nicely just by explicitly passing req and res around as necessary or by returning a stream and piping that to res as the case may warrant.
Also, writing a middleware means you create a module that doesn't work with the Node eco-system. It only works with express/Connect.
(inspired by isaacs)
- No frameworks - Everything is done using small, simple, standalone modules that work with vanilla Node.js http servers. Also no test frameworks or control flow library.
- Unceremonious MVC - No big MVC class heirarchy. Just have the route handler get some data, then hand it off to a template. Simpler is better.
- Ridiculous speed - This site should be surprisingly fast. Towards that end, things are cached and served from memory whenever possible, and ETagged for browser-cacheablility.
- DRY Dependencies - If multiple different routes all have to keep doing the same thing, then they should either be the same route, or the repeated bits belong in a dependency.
- No lib folder - If you would put it in
lib/, then it belongs in a separate module.
server.js # the starting point of our server routes/ # each request will ended up in one of those index.js # request for '/' static.js # request for static files templates/ # server-side templates index.ejs # homepage - showing list of users contact-partial.ejs # each contact config/ # single entry point for dependencies: development.js # hostnames, dbs, external api etc prod.js test.js db/ # db related stuff db.js # access to a real db (just a json file for this example) mock-db.js # access to mocked db. used for unit tests contacts.json # our DB test # unit tests with tape client # client-side tests with the help of browserify saveUser.js server index.js bin/ deploy # deploy script restart # post-deploy script restart-dev # stylus and browserify and restart server on file change .jshintrc # jshist options deploy.conf # deployment config file
Modules being used
- routes - routing
- templar - agnostic templating
- ejs - js templates
- st - serving static files
- error-page - send error pages
- browserify - to use a node-style require() to organize your browser code
- node-dev - restart the server on server files change
- tape - browser and server unit tests
(Please let me know of any helpful modules that works with vanilla http server)
npm install npm run watchify & # build browserify when a js client-side file changes npm start # run server.js with node-dev. it will restart the app when a server side file changes
Compile stylus to css
npm run style
(if your tests are hitting the server you need to run it first) npm test # use tape binary to run all tests
node test/server/index.js # run a single test
bin/deploy qa # deploy to qa host bin/deploy prod # deploy to prod host
- Node Style Guide - http://nodeguide.com/style.html
- Use Jshint
- Single entry point for dependencies (easy to mock when testing)