abstract-nested-router
v0.2.1
Published
Minimal nested-routing impl!
Downloads
424
Maintainers
Readme
Abstract Nested Router
It tries to capture all matching routes from its root.
import Router from 'abstract-nested-router';
const r = new Router();
r.add('/', { key: 'Home' });
r.mount('/', () => {
r.add('/foo', { key: 'JustFoo' });
r.mount('/foo', () => {
r.add('/static', { key: 'StaticOne' });
r.mount('/nested', () => {
r.add('/', { key: 'NestedRoot' });
r.add('/:value', { key: 'NestedValue' });
});
r.add('/:bar', { key: 'AndNested' });
});
r.add('/baz', { key: 'Baz' });
r.add('/buzz', { key: 'Buzz' });
r.mount('/buzz', () => {
r.add('#test', { key: 'Anchor' });
r.add('#:quux', { key: 'Hashed' });
});
r.add('/*any', { key: 'Fallback' });
});
In the latter example catch
is resolved just after the previous failure of /x/y/z/0
because we're trying at least twice.
API
Available methods:
resolve(path, cb)
— Progressively finds and invoke callback with(err, routes)
as input, useful for third-party integrations, e.g. yrvmount(path, cb)
— Allow to register routes under the same routefind(path[, retries])
— Look up routes by path, in case of failure try passingretries
as trueadd(path[, routeInfo])
— Register a single route by path, additional info will be returned on matchrm(path)
— Remove a single route by full-path, it will fail if given route is not registered!
Options:
While routeInfo
can include anything, but special keys are considered:
key
— Unique identity for any route handlerexact
— Tell if routing should match exactly or not
Params
By default all segments are optional, e.g. /a/:b/:c
matches with /a
, /a/x
and /a/x/y
so you can say :b
and :c
are optional parameters.
More advanced cases would require fragments to be optional, e.g. /:foo(-bar)
matches with /x
and /x-bar
because -bar
is an optional fragment.
In the latter case params.foo
will always be x
regardless if -bar
is appended, if you want to match bar
then use /:foo(-:suffix)
instead.
Splat parameters will consume the rest of the segments/fragments if they're present, e.g.
/x*y
captures anything that begins withx
and stores it onparams.y
so it matches/xy
,/xabc
,/x/y
,/x/a/b/c
and so on.
Every parameter can hold simple regex-like patterns, e.g. /:id<\d+>
Supported patterns:
/:x
and/*y
are optional segments and they cannot be empty<...>
to hold regex-like patterns,-$.
are escaped,/
is forbidden(...)
are used to mark fragments as optional, it translates to(?:...)?
Please avoid
/
inside(...)
or<...>
as they will fail loudly!
Nesting
Consider the following examples:
// 1. regular
r.add('/a');
r.add('/a/:b');
r.add('/a/:b/:c');
// 2. nested
r.mount('/a', () => {
r.mount('/:b', () => {
r.add('/:c');
});
});
// 3. concise
r.add('/a/:b/:c');
In the former way (1) we're declaring each route-level by hand, however they can be expressed at once as that latter one (3) which is more concise.
The middle form (2) is a shortcut to produce concise routes.
So which one is the best? It depends on the context:
- Use concise routes to share the same
routeInfo
on all segments, it will be applied only if it's not yet defined on the route. - Use nested routes to use shared paths, it's convenient for creating stacks of context while mounting routes, etc.
- Use regular routes to gain full control over its definition, this way each route can have its own separated context.
Routes are sorted and matched by priority and type, routes with splat params will be tried last. As more static and with less parameters the route will be matched sooner!