Home

Awesome

Decouter

Coverage Status Build

Decouter offers two main functions

By separating the pure resolution from the side-effects, it becomes really nice and easy to test your route logic for your app in isolation.

Resolution

import { resolve } from 'decouter'

const routes = {
  foo: {
    bar: true
  , baz: '/foo/bar'
  }
}

const { url, params } = resolve(routes)({ url: '/foo/bar' })
assert(url, '/foo/bar')

const { url, params } = resolve(routes)({ url: '/foo/baz' })
assert(url, '/foo/bar')

For variable segments, just prepend the key with a :. You can then access the value using a function:

const routes = {
  foo: {
    ':bar': bar => bar == 'boo' ? true : '/foo/boo'
  }
}

const { url, params } = resolve(routes)({ url: '/foo/bar' })
assert(url, '/foo/boo')

You can have a default (:) handler for when there is no further segment to match, or other fixed and variable handlers didn't match:

const routes = {
  foo: {
    ':fail1': false
    ':fail2': false
    ':' true
  }
}

const { url, params } = resolve(routes)({ url: '/foo/bar' })
assert(url, '/foo/bar')

You can also do relative redirects (.., ../). Here is an example with multi-level variables:

const routes = {
  ':org': org => !isValidOrg(org) ? '..' : {
    ':repo': repo => !isValidRepo(repo) ? '..' : true
  }
}

const { url, params } = resolve(routes)({ url: '/valid-org/valid-repo' })
assert(url, '/valid-org/valid-repo')

const { url, params } = resolve(routes)({ url: '/valid-org/invalid-repo' })
assert(url, '/valid-org')

const { url, params } = resolve(routes)({ url: '/invalid-org' })
assert(url, '/')

Normally you will want to do some auth before proceeding a level deeper. You can access the request object as the first parameter of functions (or the second for variable functions):

const isLoggedIn = req => req.token === 'valid'

const routes = {
  dashboard: req => !isLoggedIn(req) ? '/login' : true
, login: req => isLoggedIn(req) ? '/dashboard' : true
}

const { url, params } = resolve(routes)({ url: '/dashboard', token: 'valid' })
assert(url, '/dashboard')

const { url, params } = resolve(routes)({ url: '/dashboard' })
assert(url, '/login')

const { url, params } = resolve(routes)({ url: '/login', token: 'valid' })
assert(url, '/dashboard')

const { url, params } = resolve(routes)({ url: '/login' })
assert(url, '/login')

Server-Side

app.use(router(routes))

This will use the same routing logic to redirect requests to the right place before proceeding.

Client-side

In your top-level component/application:

function app(node, { routes }){
  const { url, params } = router(routes) 
}

This will use the same routing logic to return the correct URL and params the user should be on. You can use the result to determine which components to draw. It will also:

You can also programmatically control navigation with:

go('/login')

Any navigation (including redirects) will emit a change event on window so you can redraw your app:

window.addEventListener('change', app.draw)