Routing

Routes can be defined anywhere inside your /app folder. A new Raindeer application generated via rain new :app_name will create app/routes.rb.

The router observes RequestEvents (HTTP requests), matches them to a Route and converts them into RouteEvents. This RouteEvent can be observed by any node and return a response.

Defining a route

You can use simple strings to define a Route. Matching HTTP requests are automatically forwarded to a render or receive method on the node.

Raindeer.router do
  route '/'
end

Then observe '/' this route in a node. See: Observing

Route Types

Raindeer.router do
  get '/'
  query '/'
  post '/'
  put '/'
  patch '/'
  delete '/'
end

Then observe '/' in a node. See: Observing

Note

Explicit routes will map their HTTP Verb to the action of the same name; GET calls :get

Multiple HTTP Verbs

Support multiple HTTP request types at once:

Raindeer.router do
  route [GET, POST] => '/:user_id'
end

Tip

Route[] is how every route is defined behind the scenes.
Example: get '/' or route GET => '/' will become Route[GET => '/']

Params

Parameters are dynamic sections of a URL that start with a colon (:) which become available as variables in a RouteEvent.

A RouteEvents @params instance variable will contain a hash of every dynamic segment.

Nested routes

Routes can be nested like so:

Raindeer.router do
  get '/users' do
    get '/:id'
  end
end

The above route is functionalty equivalent to:

Raindeer.router do
  get '/users/:id'
end

Mid Nodes

Both configurations trigger a RouteEvent for the '/users/:id' path, however the nested example will trigger an additional RouteEvent for the '/users' path. Whether there are any observers for that path is another question and it's perfectly okay to leave an event unobserved.

Observe part of a path with side_effect:

class WatchfulEye < LowNode
  observe '/users'

  # Will not be called on '/users' but on '/users/*'.
  def side_effect(event: RouteEvent)
    # Conditionally override the response or log etc.
  end
end

Special events

While matching a request with a route is the most common use-case, additional events are triggered in the router lifecycle. These events happen in the following order:

  1. RouteEvent
  2. WildcardEvent
  3. StatusEvent

Wildcard events

'/*' Represents every unrouted HTTP request. When this route is defined then you can observe '/*' and receive a WildcardEvent.

Tip

Return nil from render to move on to the next observer

Wildcard events occur after routing so that routing is as fast as possible. If you want to do something before every request then redefine RequestEvent observers.

Status events

When no route is found a StatusEvent will be triggered, such as a "404". Observe the Status type followed by the status code; observe Status[404] and receive a StatusEvent when things go wrong.

Architecture

The router is a prefix tree! Also known as a Trie. It's pretty performant.

Rain::Router is event-driven like other core components. It observes RequestEvents and creates RouteEvents when a URL matches one of the defined routes.

You can call Raindeer.router in multiple files per "feature"; this is in keeping with the compositional nature of Raindeer... but by all means feel free to collate all your routes into one file if that's what you prefer.