A router that works on the client and server (isomorphic) and is lightweight as f*. Routes, callbacks are shared between client and server. Package has no dependencies(!). Pure ES6 JS. You will still be able to add client/server specific calls.
I have been using expressjs and pagejs for a while but I wasn't satisfied. Although they look very similar (similar function footprint) having my routes defined in a single place turned out to be a tricky business. I tried it with currying my functions (separate the paths, which are the thing you actually wanna unify) and came close. The nature of express is unfortunately that it internally uses the this
keyword. My curried functions would make it lose context and you had to start using .call
or .apply
,blegh, ugly...
Besides that I only needed a router and not a full fletched express kinda tool. Performance gain (express isn't the quickist kid on the block you know) and ease of unifying my routes made me write this package.
- Added some tests, because hey, who will take you seriously if you don't...
- Added clientside redirect (see redirect for examples)
- Added fallback for routes that don't match a pattern
- Added most HTTP methods
- Added some console.warnings so you don't look for an hour when a pattern match couldn't be made
- Updated the docs
NOTE: the response object is only available on the server, you need to call it explicitly when doing some routes, otherwise your server hangs. But if you are building a single page app (SPA) you wanna send the index.html anyway.
Missing something, bugs, you know Github right...
.
+- client
| +- main.js
+- server
| +- main.js
+- shared
+- router.js
I browserify the client and shared folder (put as much as I can in the shared folder).
document.addEventListener("DOMContentLoaded", event => {
const router = require('../shared/router')();
});
// Run any http module (spdy, https, etc)
const http = require('http');
const server = http.createServer();
const router = require('../shared/router')(server);
// Define some server only calls
router.get('/somepath', (request, response) => {
// do something
});
http.listen(8080);
module.exports = function(server) {
const router = require('cs-router')(server);
router.get('/someroute/:id', (request, response) => {
// Do stuff, for example some redux action generator calls
});
router.post('/otherroute', (request, response) => {
// handle..
});
router.delete('/otherroute', (request, response) => {
// handle..
});
router.update('/otherroute', (request, response) => {
// handle..
});
router.put('/otherroute', (request, response) => {
// handle..
});
router.options('/otherroute', (request, response) => {
// handle..
});
router.noMatch('/otherroute', (request, response) => {
// handle..
});
// Called on all routes before the specific path call
router.before((request, response) => {
// handle
});
// Called on all routes after the specific path call
router.after((request, response) => {
// handle
})
}
router.get('/somepath/:id', (request, response) => {
console.log(request.params.id) // 1;
});
http.get('/somepath/1');
The router has a coded way of redirecting urls, this build in functionality works only on the clientside, but for completeness I will give an example how you can achieve some redirects on the serverside (for example after an OAuth call to a third party)
router.redirect('/newpath'); // Pretty simple stuff
router.get('/newpath', (request, response) => {
// Some stuff
});
router.get('/somepath', (request, response) => {
response.end();
http.request({ // Standard node stuff, or use a lib like request...
path : '/newpath',
host : 'localhost',
port : '4443',
method : 'get'
});
});
Sometimes it might be handy to carry over some variables from one hook to another, for example when you wanna create a new redux store on each serverside request.
router.before((request, response) => {
// create a store somehow
store = createStore();
// The return value needs to be an object
return {
store
}
});
router.get('/somepath', (request, response, args) => {
console.log(args.store) // Store is available
});
Arguments carried over like this will be destroyed after a full request update cycle, that is after the last after statement has run (if hooked into)
Sometimes you might have a request that cannot be matched. Assigning the noMatch callback will let you work with these
router.noMatch((request, response) => {
// Handle requests that could not be matched
});
npm run test // in the package obviously