Combo.Router (combo v0.10.0)

View Source

Defines a router.

The router provides a set of macros for defining routes which dispatch requests to specific plugs.

Examples

defmodule MyApp.Web.Router do
  use Combo.Router

  get "/health", MyApp.Web.HealthCheck, []
  get "/pages/:page", MyApp.Web.PageController, :show
end

Routes

get/3, post/3, put/3, and other macros named after HTTP verbs are used to define routes. For example:

get "/", MyApp.Web.PageController, :home

defines a route that matches a GET request to / and dispatches the request to plug MyApp.Web.PageController with opts :home.

Path parameters

Path parameters capture values from the URL. There're several types of them:

  • segment parameters
  • partial segment parameters
  • catch-all parameters

Segment parameters

Segment parameters capture an entire path segment.

Define them in the route path with : followed by a name. And, the captured values are strings, cast them yourself if you need other data types.

For example:

get "/pages/:page", MyApp.Web.PageController, :show

When a request hits the route with the URL "/pages/hello", the router populates conn.path_params["page"] with "hello".

Partial segment parameters

Partial segment parameters capture a trailing portion from within a single path segment.

Define them in the route path with : followed by a name. And, the captured values are strings, cast them yourself if you need other data types.

For example:

get "/user-:name", MyApp.Web.UserController, :show

When a request hits the route with the URL "/user-john", the router populates conn.path_params["name"] with "john".

Catch-all parameters

Catch-all parameters capture one or more remaining path segments.

Define them in the route path with * followed by a name. And, the captured values are a list of strings - one per path segment, cast them yourself if you need other data types.

For example:

get "/files/*path", MyApp.Web.FileController, :show

When a request hits the route with the URL "/files/images/logo.png", the router populates conn.path_params["path"] with ["images", "logo.png"].

Accessing path parameters

To access path parameters, use conn.params or conn.path_params.

Or, pattern match directly in the controller's action:

defmodule MyApp.Web.PageController do
  def show(conn, %{"page" => page}) do
    # ...
  end
end

Combining different types of path parameters

All these types of path parameters can be combined, with the only restriction being that catch-all parameters must appear at the end.

Ordering routes

Routes are matched from top to bottom.

For example, the request with the URL "/pages/hello" will never hit the second route, because it always hits the first route.

get "/pages/:page", MyApp.Web.PageController, :show
get "/pages/hello", MyApp.Web.PageController, :hello

Route helpers

Combo generates a Helpers module that provides helper functions for building paths or URLs from your routes.

Helpers are automatically generated based on the module name of plug. For example, the route:

get "/pages/:page", PageController, :show

will generate the following helper:

MyApp.Web.Router.Helpers.page_path(conn, :show, "hello")
"/pages/hello"

MyApp.Web.Router.Helpers.page_path(conn, :show, "hello", some: "query")
"/pages/hello?some=query"

MyApp.Web.Router.Helpers.page_url(conn, :show, "hello")
"http://example.com/pages/hello"

MyApp.Web.Router.Helpers.page_url(conn, :show, "hello", some: "query")
"http://example.com/pages/hello?some=query"

If the route contains catch-all parameters, parameters for those should be given as a list:

MyApp.Web.Router.Helpers.file_path(conn, :show, ["images", "logo.png"])
"/file/images/logo.png"

The helper can also be customized with the :as option. Given the route:

get "/pages/:page", PageController, :show, as: :special_page

the helper will be:

MyApp.Web.Router.Helpers.special_page_path(conn, :show, "hello")
"/pages/hello"

See Combo.Router.Helpers for more information.

Scopes

It is very common to namespace routes under a scope. For example:

scope "/", MyApp.Web do
  get "/users/:id", UserController, :show
  get "/posts/:id", PostController, :show
end

This syntax is convenient to use, since you don't have to repeat MyApp.Web. prefix on all routes.

You can also use path parameters. For example:

scope "/api/:version", MyApp.Web do
  get "/pages/:id", PageController, :show
end

See scope/2 for more information.

Pipes

Once a request arrives at the router, it passes through a series of pipes before being dispatched to the matched route.

Pipes are only invoked if a route is matched. If no route matches, no pipe is invoked.

A pipe can be either a plug, or a pipeline.

Pipe names

Every pipe has a name.

  • For function plugs, the pipe name is the atom name of the function.
  • For module plugs, the pipe name is the module name.
  • For pipelines, the pipe name is the atom name of the pipeline.

Defining pipes

To define a plug, see Plug for more information.

To define a pipeline, see pipeline/2 for more information.

Using pipes

See pipe_through/1 for more information.

Resources

Combo.Router doesn't provide resources related macro that allows to generate "RESTful" routes to a given resource. For clarity, we recommend defining them explicitly.

An example for resources:

get "/users", UserController, :index
get "/users/new", UserController, :new
post "/users", UserController, :create
get "/users/:id", UserController, :show
get "/users/:id/edit", UserController, :edit
patch "/users/:id", UserController, :update
put "/users/:id", UserController, :update
delete "/users/:id", UserController, :delete

An example for singleton resources:

get "/user/new", UserController, :new
post "/user", UserController, :create
get "/user", UserController, :show
get "/user/edit", UserController, :edit
patch "/user", UserController, :update
put "/user", UserController, :update
delete "/user", UserController, :delete

Listing routes

Combo ships with a mix combo.routes task that formats all routes in a given router. We can use it to list all routes included in the router.

Summary

Reflection

Returns the compile-time route info and runtime path params for a request.

Functions

Defines a route to handle a connect request to the given path.

Defines a route to handle a delete request to the given path.

Forwards a request at the given path to a plug.

Defines a route to handle a get request to the given path.

Defines a route to handle a head request to the given path.

Defines a route based on an arbitrary HTTP method.

Defines a route to handle a options request to the given path.

Defines a route to handle a patch request to the given path.

Defines pipes to apply within the current scope.

Defines a pipeline, which is a named collection of plugs.

Adds a plug into a pipeline.

Defines a route to handle a post request to the given path.

Defines a route to handle a put request to the given path.

Returns all routes information from the given router.

Defines a scope.

See scope/2 for more information.

See scope/2 for more information.

Expands a module with the current scope's module.

Defines a route to handle a trace request to the given path.

Reflection

route_info(router, method, path)

Returns the compile-time route info and runtime path params for a request.

The path can be either a string or the path_info segments.

A map of metadata is returned with the following keys:

  • :log - the configured log level, such as :debug.
  • :path_params - the map of runtime path params.
  • :pipes - the pipes for the route's scope, such as [:browser].
  • :plug - the plug to dispatch the route to, such as MyApp.Web.PostController.
  • :plug_opts - the options to pass when calling the plug, such as :index.
  • :route - the string route pattern, such as "/posts/:id".

Examples

iex> Combo.Router.route_info(MyApp.Web.Router, "GET", "/posts/123")
%{
  log: :debug,
  path_params: %{"id" => "123"},
  pipe_through: [:browser],
  plug: MyApp.Web.PostController,
  plug_opts: :show,
  route: "/posts/:id",
}

iex> Combo.Router.route_info(MyRouter, "GET", "/not-exists")
:error

Functions

connect(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a connect request to the given path.

connect "/events/:id", EventController, :action

See match/5 for options.

delete(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a delete request to the given path.

delete "/events/:id", EventController, :action

See match/5 for options.

forward(path, plug, plug_opts \\ [], router_opts \\ [])

(macro)

Forwards a request at the given path to a plug.

This is commonly used to forward all subroutes to another Plug. For example:

forward "/admin", SomeLib.AdminDashboard

The above will allow SomeLib.AdminDashboard to handle /admin, /admin/foo, /admin/bar/baz, and so on. Furthermore, SomeLib.AdminDashboard does not to be aware of the prefix it is mounted in. From its point of view, the routes above are simply handled as /, /foo, and /bar/baz.

A common use case for forward is for sharing a router between applications or breaking a big router into smaller ones. However, in other for route generation to route accordingly, you can only forward to a given Combo.Router once.

The router pipes will be invoked prior to forwarding the connection.

Examples

scope "/", MyApp do
  pipe_through [:browser, :admin]

  forward "/admin", SomeLib.AdminDashboard
  forward "/api", ApiRouter
end

get(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a get request to the given path.

get "/events/:id", EventController, :action

See match/5 for options.

head(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a head request to the given path.

head "/events/:id", EventController, :action

See match/5 for options.

Compatibility with Plug.Head

Templates provided by combo_new include Plug.Head in their endpoint, which converts HEAD requests into regular GET requests. Therefore, if you intend to use head/4 in your router, you need to move Plug.Head to your router in a way it does not conflict with the paths given to head/4.

match(verb, path, plug, plug_opts, options \\ [])

(macro)

Defines a route based on an arbitrary HTTP method.

Useful for defining routes not included in the built-in macros.

The catch-all verb, :*, may also be used to match all HTTP methods.

Options

  • :scoped_module - whether to apply the scoped module to the route. Defaults to true.
  • :as - the name as an atom or a string, to override the default naming for the route helpers. If nil, it will not generate route helpers for this route.
  • :private - the private data as a map to merge into the connection when a route matches. Default to %{}.
  • :assigns - the data as a map to merge into the connection when a route matches. Default to %{}.
  • :log - the level to log the route dispatching under. Defaults to :debug. Can be set to false to disable the logging. Route dispatching logging contains information about how the route is handled (which plug is called, what plug_opts are given, what parameters are available and which pipes are used). It is separated from the plug level logging. To alter the plug log level, please see https://hexdocs.pm/combo/Combo.Logger.html#module-dynamic-log-level.

Examples

# match the GET method
match :get, "/events/:id", EventController, :get

# match all methods
match :*, "/any", CatchAllController, :any

options(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a options request to the given path.

options "/events/:id", EventController, :action

See match/5 for options.

patch(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a patch request to the given path.

patch "/events/:id", EventController, :action

See match/5 for options.

pipe_through(pipes)

(macro)

Defines pipes to apply within the current scope.

The pipes are specified by their names.

Examples

pipe_through [:browser, :require_authenticated_user]

Multiple invocations

pipe_through/1 can be invoked multiple times within the same scope. Each invocation appends new pipes, which are applied to all routes after the pipe_through/1 invocation. For example:

scope "/" do
  pipe_through [:browser]
  get "/", HomeController, :index

  pipe_through [:require_authenticated_user]
  get "/settings", UserController, :edit
end

/ applies :browser only, while /settings applies both :browser and :require_authenticated_user. To avoid confusion, we recommend to use a single pipe_through at the top of each scope:

scope "/" do
  pipe_through [:browser]
  get "/", HomeController, :index
end

scope "/" do
  pipe_through [:browser, :require_authenticated_user]
  get "/settings", UserController, :edit
end

pipeline(name, do_block)

(macro)

Defines a pipeline, which is a named collection of plugs.

Pipelines must be defined at the root of router.

Examples

pipeline :api do
  plug :put_current_user
  plug :dispatch
end

A scope can use this pipeline as:

scope "/" do
  pipe_through :api
end

See pipe_through/1 for more information.

plug(plug, opts \\ [])

(macro)

Adds a plug into a pipeline.

See pipeline/2 for more information.

post(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a post request to the given path.

post "/events/:id", EventController, :action

See match/5 for options.

put(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a put request to the given path.

put "/events/:id", EventController, :action

See match/5 for options.

routes(router)

Returns all routes information from the given router.

scope(arg, do_block)

(macro)

Defines a scope.

Scopes are for grouping routes under a common path prefix or a common module prefix.

Examples

scope path: "/api/v1", module: API.V1 do
  get "/pages/:id", PageController, :show
end

The generated route above will match on the path "/api/v1/pages/:id" and will dispatch requests to plug API.V1.PageController with opts :show. A named helper api_v1_page_path will also be generated.

Options

  • :path - the path scope as a string.
  • :module - the module scope as a module name. When set to false, it resets all nested :module options.
  • :as - the route naming scope as a string or an atom. When set to false, it resets all nested :as options.
  • :private - the private data as a map to merge into the connection when a route matches.
  • :assigns - the data as a map to merge into the connection when a route matches.
  • :log - the level to log the route dispatching under. Defaults to :debug. Can be set to false to disable the logging. Route dispatching logging contains information about how the route is handled (which plug is called, what plug_opts are given, what parameters are available and which pipes are used). It is separate from the plug level logging. To alter the plug log level, please see https://hexdocs.pm/combo/Combo.Logger.html#module-dynamic-log-level.

Shortcuts

A scope can also be defined with shortcuts.

# specify path and module
scope "/api/v1", API.V1 do
  get "/pages/:id", PageController, :show
end

# specify path, module and options
scope "/api/v1", API.V1, as: :api, do
  get "/pages/:id", PageController, :show
end

# specify path only
scope "/api/v1" do
  get "/pages/:id", API.V1.PageController, :show
end

# specify path and options
scope "/api/v1", as: :api, do
  get "/pages/:id", API.V1.PageController, :show
end

# specify module only
scope API.V1 do
  get "/pages/:id", PageController, :show
end

# specify module and options
scope API.V1, as: :api, do
  get "/pages/:id", PageController, :show
end

scope(arg1, arg2, do_block)

(macro)

See scope/2 for more information.

scope(arg1, arg2, arg3, do_block)

(macro)

See scope/2 for more information.

scoped_module(module)

(macro)

Expands a module with the current scope's module.

It's useful when you need to reference scoped modules in other contexts, such as passing them as options.

Examples

scope "/admin", Admin do

# UserController is expanded to Admin.UserController
get "/users", ProxyPlug, handler: scoped_module(UserController)

end

trace(path, plug, plug_opts, options \\ [])

(macro)

Defines a route to handle a trace request to the given path.

trace "/events/:id", EventController, :action

See match/5 for options.