1.0.3 • Published 3 years ago

route-descriptor v1.0.3

Weekly downloads
1
License
MIT
Repository
github
Last release
3 years ago

route-descriptor

Build Coverage Version Downloads Donate

This package provides an interface to statically represent routing configuration in your application. It lets you establish a single source of truth for generating links, which avoids code duplication and makes refactoring easier. Works best with TypeScript.

Download

  • npm: npm i route-descriptor

Features

  • Supports static and dynamic routes
  • Supports positional and query parameters
  • Parameter validation via TypeScript
  • Works in browser and NodeJS

Usage

Routes with parameters

The following example creates a dynamic route called product with expected parameters declared in ProductParams. The route itself is a function that can be invoked to resolve the actual URL:

import { route } from 'route-descriptor';

interface ProductParams {
  id: number;
}

const product = route<ProductParams>('/products/:id');

const href = product({ id: 3 }); // '/products/3'

Routes with query parameters

Any parameters that don't match with keys in the route path are automatically mapped as query parameters:

import { route } from 'route-descriptor';

interface ProductParams {
  id: number;
  showComments?: boolean;
}

const product = route<ProductParams>('/products/:id');

const href = product({
  id: 3,
  showComments: true
}); // '/products/3?showComments=true'

Routes without parameters

You can also create a static route, i.e. such that doesn't expect any parameters. Invoking it simply returns the path without any replacements:

import { route } from 'route-descriptor';

const home = route('/home');

const href = home(); // '/home'

Routes with optional positional parameters

Some routes may have positional parameters which are optional. They need to be marked with the ? modifier in the path template:

import { route } from 'route-descriptor';

interface ProfileParams {
  id: number;
  name?: string;
}

const profile = route<ProfileParams>('/profile/:id/:name?');

const href = profile({
  id: 13
}); // '/profile/13'

Retrieving the path

Once the route is created, you can get the original path back too. This may be convenient when plugging route-descriptor into a routing library of your choice:

import { route } from 'route-descriptor';

const profile = route<ProfileParams>('/profile/:id/:name?');

const path = profile.path; // '/profile/:id/:name?'

Combining with react-router

It's possible to use route-descriptor with pretty much any client-side routing library. For example, here is how to integrate it with react-router:

  • ./src/routes.ts:
import { route } from 'route-descriptor';

interface ProductParams {
  id: number;
  showComments?: boolean;
}

interface ProfileParams {
  id: number;
  name?: string;
}

export default {
  home: route('/home'),
  product: route<ProductParams>('/products/:id'),
  profile: route<ProfileParams>('/profile/:id/:name?')
};
  • ./src/App.tsx:
import { Route, Switch, BrowserRouter, Link } from 'react-router-dom';
import routes from './routes';

function Home() {
  return (
    <div>
      <Link to={routes.home()}>Home</Link>
      <Link to={routes.profile({ id: 1, name: 'JohnDoe' })}>My Profile</Link>
      <Link to={routes.product({ id: 3, showComments: true })}>Random Product</Link>
    </div>
  );
}

function Product() {
  /* ... */
}

function Profile() {
  /* ... */
}

export default function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path={routes.profile.path} component={Profile} />
        <Route path={routes.product.path} component={Product} />
        <Route path={routes.home.path} component={Home} />
      </Switch>
    </BrowserRouter>
  );
}

As you can see, the routes are defined in a single place (the routes.ts module) from which they referenced throughout the application. This makes changing the paths and route parameters easy in the future, as you don't have to worry about updating URLs in every anchor tag.

TypeScript integration

This package is most useful when paired with TypeScript, as it provides static validation for parameters. For example, all of the following incorrect usages produce errors during compilation:

import { route } from 'route-descriptor';

const home = route('/home');
home({ id: 5 }); // <- error (static route can't accept parameters)

const product = route<ProductParams>('/products/:id');
product(); // <- error (dynamic route requires parameters)
product({ showComments: true }); // <- error (missing 'id')
product({ id: 3, name: 'apple' }); // <- error (unexpected 'name')

If you want, it's also possible to use route-descriptor with plain JavaScript, which is still useful for establishing a single source of truth, but doesn't help with parameter validation:

import { route } from 'route-descriptor';

// Works in plain JS
const product = route('/products/:id');
const href = product({ id: 3 });