@medeor/declarative-route-builder v0.2.0
Declarative Route Builder
A declarative way to build the routes for your frontend application. Decouple the routing structure from the UI and navigation libraries, split routes in cohesive modules and then aggregate them all into a complete route hierarchy.
Getting Started
First, install the library using your favorite package manager:
npm i @medeor/declarative-route-builder --save
or
yarn add @medeor/declarative-route-builder
Creating your routing tree
Create a Tree and add routes and navigators into it. The Tree will compute the routes into the navigators, and provide a json-like structure for you to iteratively render the routing hierarchy using your UI navigation library.
import {
Route,
StackNavigator,
Tree,
} from '@medeor/declarative-route-builder'
const router = new Tree();
router.add('namespace', new Route('main', MainComponent));
router.add('namespace', new StackNavigator('settings', [
new Route('profile-settings', ProfileComponent),
new Route('billing-settings', BillingComponent)
]))
export const routes = router.getRoutes('namespace')
Namespaces
Use namespaces to control access flow of your routes.
// infra/routes.ts
import {
Route,
Tree,
} from '@medeor/declarative-route-builder'
const router = new Tree({
public: [
new Route('signup', <SignupComponent />),
],
private: [
new Route('profile', <ProfileComponent />)
]
})
export default router;
or
// infra/routes.ts
import {
Route,
Tree,
} from '@medeor/declarative-route-builder'
const router = new Tree()
.add('public', new Route('signup', <SignupComponent />))
.add('private', new Route('profile', <ProfileComponent />));
export default router;
then
// presentation/Routes.tsx
import React from 'react'
// ...
import routes from '@infra/routes'
const Routes: React.FC = () => {
const { isLogged } = useAuthContext()
return isLogged ?
<RenderRoutes tree={routes.getRoutes('private')} > :
<RenderRoutes tree={routes.getRoutes('public')} >
}
export default Routes
Navigators
You can use navigators that group routes inside them. Navigators can have different types, mostly used when building routes using React Native, or nesting routes on the web.
import {
BottomTabsNavigator,
Route,
StackNavigator,
Tree,
} from '@medeor/declarative-route-builder'
export const routes = new Tree()
.add('main', new StackNavigator(
'Main',
[
new Route('Welcome', Welcome),
new BottomTabsNavigator(
'Home',
[
new Route('Feed', Feed),
new Route('Setttings', Settings)
]
)
]
))
Modules
Split your routes into different files, then group them into one main navigator to split your code into cohesive modules.
// Authentication Module
import {
Route,
Tree,
} from '@medeor/declarative-route-builder'
export const authenticationRoutes = new Tree()
.add('main', new Route('signin', Signin))
.add('main', new Route('signup', Signup))
// Users module
import {
Route,
Tree,
} from '@medeor/declarative-route-builder'
export const userRoutes = new Tree()
.add('main', new Route('user', User))
.add('main', new Route('users', Users))
// Main module
import { Tree } from '@medeor/declarative-route-builder'
import { authenticationRoutes } from '@modules/authentication/routes'
import { userRoutes } from '@modules/user/routes'
const router = new Tree()
.use(authenticationRoutes)
.use(userRoutes)
Result object
The routes you create result in a JSON-like object you can iterate over to render your own routing hierarchy.
import {
BottomTabsNavigator,
Route,
StackNavigator,
Tree,
} from '@medeor/declarative-route-builder'
const routes = new Tree()
.add('main', new StackNavigator(
'Main',
[
new Route('Welcome', 'Welcome'),
new Route('Feed', 'Feed'),
]
))
// routes.getRoutes('main')
[
{
key: 'Main',
routes: [
{
key: 'Welcome',
target: 'Welcome',
type: 'route',
},
{
key: 'Feed',
target: 'Feed',
type: 'route',
}
],
type: 'navigator',
navigator: 'stack'
}
]
Type checking
The Tree class can receive a generic that enforces various types, such as allowed namespaces, route names, and more. You can extend the base RouterSpecifications
interface and replace any type with a matching one.
// From @medeor/declarative-route-builder
interface RouterSpecifications {
icons: string;
routeKeys: string;
routeTarget: any;
navigatorKeys: string;
nodeTitles: string;
namespaces: string;
}
// In your project
interface CustomSpecifications extends RouterSpecifications {
icons: 'close' | 'back';
routeKeys: 'Home' | 'Auth';
routeTarget: React.ComponentType<any>;
navigatorKeys: 'MainTabs';
nodeTitles: 'User' | 'Login';
namespaces: 'public' | 'private';
}
// You can pass the CustomSpecification via generic to each routing class for type-checking
const tree = new Tree<CustomSpecification>()
.add('public', new Route<CustomSpecification>('Home', HomeComponent))
// Or you can use the tree's factories to avoid passing the generic to each class
const tree = new Tree<CustomSpecification>();
tree.add('public', tree.Route('Auth', AuthComponent, { icon: 'close' }))