@idio/router

The Router Utility For The Idio Web Server With Automatic Initialisation From Folders And Live Reload.

Usage no npm install needed!

<script type="module">
  import idioRouter from 'https://cdn.skypack.dev/@idio/router';
</script>

README

@idio/router

npm version

@idio/router Is The Router Utility For The Idio Web Server With Automatic Initialisation From Folders And Live Reload.

yarn add @idio/router

Table Of Contents

API

The package is available by importing its default function:

import initRoutes, { watchRoutes } from '@idio/router'

async initRoutes(
  router: !_goa.Router,
  dir: string,
  config=: !RoutesConfig,
): !WatchConfig

The init function will scan files in the passed dir folder and add routes found for each method to the router. The default dir is src/routes and the config should be passed to control how the middleware and aliases are set up for each method.

  • router* !_goa.Router: An instance of the router.
  • dir* string: The directory from where to init routes.
  • config !RoutesConfig (optional): Additional configuration.

There are some rules when using this method:

  1. Each route module should export the default function which will be initialised as the middleware.
  2. The modules can also export the aliases property with an array of strings that are aliases for the route (alternatively, aliases can be specified via the configuration object — or when both ways are used, they are combined).
  3. The exported middleware property specifies any middleware chain constructor that will take precedence over the method middleware chain constructor from the config. When strings are passed, the middleware functions will be looked up in the middleware object returned by the idio's start method and passed in the configuration.
  4. If the exported middleware property is an array, the route will be the last one in the chain call. Otherwise, exporting a middleware chain constructor as a function allows to control the order of execution.

RoutesConfig: Options for the router.

Name Type & Description
middlewareConfig !Object<string, !chainRoute>
The method-level middleware configuration: for each method it specifies how to construct the middleware chain. If the string is found in the chain, the middleware will be looked up in the middleware object.
middleware !ConfiguredMiddleware
The configured middleware object return by the Idio's start method.
aliases !Object<string, !Array<string>>
The map of aliases. Aliases can also be specified in routes by exporting the aliases property.
filter (filename: string) => boolean
The filter for filenames. Defaults to importing JS and JSX.
filename* string: The filename.

For example, we can specify 1 GET and 1 POST route in the example/routes directory:

example/routes
├── get
│   └── index.js
└── post
    └── example.js

example/routes/get/index.js

export default async (ctx) => {
  const { test } = ctx
  ctx.body = `example get response: ${test}`
}

export const aliases = ['/']

// The router util will lookup the middleware by its name.
// The constructor is used to control the order.
export const middleware = (route) => {
  return ['example', route]
}

// Another way to write middleware is to use a plain array.
/* export const middleware = ['example'] */

example/routes/post/example.js

export default async (ctx) => {
  const { message } = ctx.request.body
  ctx.body = `example post response: ${message}`
}

// The aliases exported in the module will extend
// the ones specified in the config
export const aliases = ['/']

Then the router can be automatically configured.

import core from '@idio/idio'
import initRoutes from '@idio/router'

const Server = async () => {
  const { app, url, router, middleware } = await core({
    bodyparser: {
      middlewareConstructor() {
        return async (ctx, next) => {
          const data = await collect(ctx.req)
          ctx.req.body = JSON.parse(data)
          ctx.request.body = JSON.parse(data)
          await next()
        }
      },
    },
    example: {
      middlewareConstructor() {
        return async (ctx, next) => {
          ctx.test = 'test'
          await next()
        }
      },
    },
  })
  await initRoutes(router, 'example/routes', {
    middlewareConfig: {
      post(route) {
        return ['bodyparser', route]
      },
    },
    aliases: {
      post: {
        '/example': ['/example.html'],
      },
    },
    middleware,
  })
  app.use(router.routes())
  return { app, url }
}
http://localhost:5000
GET /
 :: example get response: test
POST "hello world" > / 
 :: example post response: hello world

chainRoute(
  route: !Middleware,
): !Array<string|!Middleware>

Receives the route and returns an ordered array of middleware.

async watchRoutes(
  watchConfig: !WatchConfig,
): !fs.FSWatcher

After the routes were initialised, it is possible to pass the value returned by the initRoutes method to the watchRoutes function to enable hot-route reload on the development environment. Every change to the module source code will trigger an update of the route including its aliases. The middleware and aliases changes are not currently implemented.

  • watchConfig* !WatchConfig: The watch config returned by the initRoutes method.
import idio from '@idio/idio'
import initRoutes, { watchRoutes } from '@idio/router'

const Server = async () => {
  const { app, url, router } = await idio({}, { port: 5001 })
  const w = await initRoutes(router, 'example/watch-routes')
  app.use(router.routes())
  let watcher
  if (process.env.NODE_ENV != 'production') {
    watcher = await watchRoutes(w)
  }
  return { app, url, watcher }
}
http://localhost:5001
GET / RESULT:
┌────────────────────────────────┐
│ [initial] example get response │
└────────────────────────────────┘
Update routes/get/index.js
⌁ example/watch-routes/get/index.js
.> hot reloaded GET /index /
GET / RESULT:
┌────────────────────────────────┐
│ [updated] example get response │
└────────────────────────────────┘

Copyright & License

GNU Affero General Public License v3.0

Art Deco © Art Deco™ for Idio 2020 Idio