All Projects → substack → Substack Flavored Webapp

substack / Substack Flavored Webapp

Here are some tiny backend node modules I like to glue together to build webapps.

Programming Languages

javascript
184084 projects - #8 most used programming language

Here are some tiny backend node modules I like to glue together to build webapps.

This example only includes the backend components.

modules

  • tcp-bind - allocate a low port before dropping privileges
  • routes - organize routing
  • ecstatic - serve static files
  • body - parse incoming form data
  • trumpet - insert html into layouts

tcp-bind

This module will allocate a port so that you can allocate as root and then drop down into a non-root user after the port has been allocated:

var alloc = require('tcp-bind');
// ...
var fd = alloc(argv.port);
if (argv.gid) process.setgid(argv.gid);
if (argv.uid) process.setuid(argv.uid);

Specify the user and group to drop into with -g and -u.

routes

The routes module is handy for decomposing the different routes of your webapp.

Create a router with:

var router = require('routes')();

then you can add routes with:

router.addRoute('/robots/:name', function (req, res, params) {
    res.end('hello ' + params.name);
});

In the http.createServer() handler function, we can dispatch our routes using:

var m = router.match(req.url);
if (m) m.fn(req, res, m.params);

and then have some fallback handlers if none of the routes matched, like to ecstatic.

ecstatic

To serve static assets out of static/, I use ecstatic:

var st = require('ecstatic')(__dirname + '/static');

and then in the http.createServer() function you can do:

st(req, res)

body

To parse form parameters, I use the body module:

body(req, res, function (err, params) { /* ... */})

or i have a nifty little wrapper function:

function post (fn) {
    return function (req, res, params) {
        if (req.method !== 'POST') {
            res.statusCode = 400;
            res.end('not a POST\n');
        }
        body(req, res, function onbody (err, pvars) {
            fn(req, res, xtend(pvars, params));
        });
    };
}

so that I can wrap an entire route handler in a post():

router.addRoute('/submit', post(function (req, res) {
    console.log(params);
    res.end('ok!\n');
}));

It might be handy to publish some of these little functions individually to npm but I haven't done that yet.

trumpet

trumpet is a nifty module for pumping content into some html at a css selector.

I use it to make a little layout function:

function layout (res) {
    res.setHeader('content-type', 'text/html');
    var tr = trumpet();
    read('layout.html').pipe(tr).pipe(res);
    return tr.createWriteStream('#body');
}

and then in static/layout.html I have an empty element with an id of body:

<html>
  <body>
    <h1>my way cool website</h1>
    <div id="body"></div>
  </body>
</body>

Also check out hyperstream, which uses trumpet but lets you specify many selectors at once.

as it grows

As the server file gets bigger, I start moving functions and route handlers into lib/ or lib/routes or someplace like that.

An example route file in lib/ would look like:

module.exports = function (req, res, params) {
    res.end('beep boop\n');
};

and then in the server.js I can do:

router.addRoute('/whatever', require('./lib/someroute.js'));

or if the route needs some extra information, I can return a function in the route instead:

module.exports = function (msg) {
    return function (req, res, params) {
        res.end(msg + '\n');
    };
};

and then in server.js:

router.addRoute('/whatever', require('./lib/someroute.js')('beep boop'));

I try to only pass in the information that a route directly needs, since that keeps the code less coupled to my application.

Try to avoid passing around an app object to everywhere since that can create huge problems later when refactoring and the code can become very coupled. It might be ok to pass a bus object around liberally so long as only handles dispatching events.

running the server

To run the server for development, do:

$ npm start

To run the server on port 80 for production, do:

$ sudo node server.js -u $USER -g $USER 
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].