phoenix-routing

Phoenix routing maps incoming HTTP requests to controller actions. The router is the entry point for all web requests and determines which controller action should handle each request. Phoenix provides powerful routing macros for RESTful resources, scopes, pipelines, and verified routes.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "phoenix-routing" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-phoenix-routing

Phoenix Routing

Phoenix routing maps incoming HTTP requests to controller actions. The router is the entry point for all web requests and determines which controller action should handle each request. Phoenix provides powerful routing macros for RESTful resources, scopes, pipelines, and verified routes.

Basic Route Declaration

Single Routes

Define individual routes using HTTP verb macros:

get "/", PageController, :home

Phoenix supports all standard HTTP verbs:

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

Routes with Dynamic Segments

Capture URL parameters using the :param_name syntax:

get "/hello/:messenger", HelloController, :show

The :messenger segment becomes available in the controller's params map.

Resource Routes

Basic Resource Declaration

Generate all standard RESTful routes with the resources macro:

resources "/users", UserController

This generates eight routes:

GET /users UserController :index GET /users/:id/edit UserController :edit GET /users/new UserController :new GET /users/:id UserController :show POST /users UserController :create PATCH /users/:id UserController :update PUT /users/:id UserController :update DELETE /users/:id UserController :delete

Limiting Resource Routes

Use :only to generate specific routes:

resources "/users", UserController, only: [:show] resources "/posts", PostController, only: [:index, :show]

Use :except to exclude specific routes:

resources "/users", UserController, except: [:create, :delete]

Aliasing Resources

Customize the route path helper name with :as :

resources "/users", UserController, as: :person

This generates path helpers like ~p"/person" instead of ~p"/users" .

Nested Resources

Create hierarchical resource relationships:

resources "/users", UserController do resources "/posts", PostController end

Generated routes include the parent resource ID:

GET /users/:user_id/posts PostController :index GET /users/:user_id/posts/:id/edit PostController :edit GET /users/:user_id/posts/new PostController :new GET /users/:user_id/posts/:id PostController :show POST /users/:user_id/posts PostController :create PATCH /users/:user_id/posts/:id PostController :update PUT /users/:user_id/posts/:id PostController :update DELETE /users/:user_id/posts/:id PostController :delete

Verified Routes

Using the ~p Sigil

Phoenix provides compile-time verified routes using the ~p sigil:

Static paths

~p"/users" ~p"/posts/new"

Dynamic segments with variables

~p"/users/#{user_id}" ~p"/users/#{user_id}/posts/#{post_id}"

Verified Routes with Structs

Pass structs directly to generate paths:

~p"/users/#{@user}"

Generates: "/users/42"

~p"/users/#{user}/posts/#{post}"

Generates: "/users/42/posts/17"

Phoenix automatically extracts the ID using the Phoenix.Param protocol.

Benefits of Verified Routes

  • Compile-time validation - Catch routing errors during compilation

  • Refactoring safety - Route changes are caught immediately

  • Type safety - Ensure correct parameter types

  • URL slug support - Easy transition to slug-based URLs

Scopes

Basic Scopes

Group routes under a common path prefix:

scope "/admin", HelloWeb.Admin do pipe_through :browser

resources "/users", UserController end

Generated paths include the scope prefix:

~p"/admin/users"

Scopes with Aliases

Reduce repetition by aliasing controller modules:

scope "/", HelloWeb do pipe_through :browser

get "/", PageController, :home resources "/posts", PostController end

Nested Scopes

Create hierarchical route organization:

scope "/api", HelloWeb.Api, as: :api do pipe_through :api

scope "/v1", V1, as: :v1 do resources "/users", UserController end

scope "/v2", V2, as: :v2 do resources "/users", UserController end end

Generated path helpers reflect the nesting:

~p"/api/v1/users" ~p"/api/v2/users"

Pipelines

Defining Pipelines

Pipelines group plugs that run for specific routes:

pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers end

pipeline :api do plug :accepts, ["json"] end

Applying Pipelines to Scopes

Use pipe_through to apply pipelines:

scope "/", HelloWeb do pipe_through :browser

get "/", PageController, :home resources "/users", UserController end

scope "/api", HelloWeb.Api do pipe_through :api

resources "/users", UserController end

Custom Plugs in Pipelines

Add application-specific plugs:

pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug HelloWeb.Plugs.Locale, "en" end

Nesting Pipelines

Compose pipelines for complex authentication flows:

pipeline :auth do plug :browser plug :ensure_authenticated_user plug :ensure_user_owns_review end

scope "/reviews", HelloWeb do pipe_through :auth

resources "/", ReviewController end

This applies the :browser pipeline first, then the authentication plugs.

Advanced Pipeline Patterns

Session Management Pipeline

Create a pipeline for session-based features:

pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug :fetch_current_scope_for_user end

defp fetch_current_scope_for_user(conn, _opts) do if id = get_session(conn, :scope_id) do assign(conn, :current_scope, MyApp.Scope.for_id(id)) else id = System.unique_integer()

conn
|> put_session(:scope_id, id)
|> assign(:current_scope, MyApp.Scope.for_id(id))

end end

Multi-tenant Routing

Assign organization context from URL parameters:

pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug :fetch_current_scope_for_user plug :assign_org_to_scope end

defp assign_org_to_scope(conn, _opts) do case conn.params["org"] do nil -> conn org_slug -> scope = conn.assigns.current_scope org = MyApp.Organizations.get_by_slug!(org_slug) assign(conn, :current_scope, Map.put(scope, :organization, org)) end end

Shopping Cart Pipeline

Fetch or create a cart for the current session:

pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug :fetch_current_scope_for_user plug :fetch_current_cart end

alias MyApp.ShoppingCart

defp fetch_current_cart(%{assigns: %{current_scope: scope}} = conn, _opts) when not is_nil(scope) do if cart = ShoppingCart.get_cart(scope) do assign(conn, :cart, cart) else {:ok, new_cart} = ShoppingCart.create_cart(scope, %{}) assign(conn, :cart, new_cart) end end

defp fetch_current_cart(conn, _opts), do: conn

Forwarding

Forward to Plugs

Delegate a path prefix to another plug or application:

defmodule HelloWeb.Router do use HelloWeb, :router

scope "/", HelloWeb do pipe_through :browser get "/", PageController, :home end

forward "/jobs", BackgroundJob.Plug end

All requests to /jobs/* are handled by BackgroundJob.Plug .

Common Forward Use Cases

Admin interface

forward "/admin", HelloWeb.AdminRouter

API documentation

forward "/api/docs", PhoenixSwagger.Plug.SwaggerUI

Background job dashboard

forward "/jobs", Oban.Web.Router

Inspecting Routes

Using mix phx.routes

View all defined routes in your application:

mix phx.routes

Output shows HTTP verb, path, controller, and action:

GET / HelloWeb.PageController :home GET /users HelloWeb.UserController :index GET /users/:id/edit HelloWeb.UserController :edit GET /users/new HelloWeb.UserController :new GET /users/:id HelloWeb.UserController :show POST /users HelloWeb.UserController :create PATCH /users/:id HelloWeb.UserController :update PUT /users/:id HelloWeb.UserController :update DELETE /users/:id HelloWeb.UserController :delete

Filtering Routes

Grep for specific routes:

mix phx.routes | grep users mix phx.routes | grep POST

Building Paths Programmatically

Static Paths

Build static paths easily:

~p"/users"

Returns: "/users"

~p"/posts/new"

Returns: "/posts/new"

Paths with Integer IDs

Interpolate IDs directly:

user_id = 42 post_id = 17 ~p"/users/#{user_id}/posts/#{post_id}"

Returns: "/users/42/posts/17"

Paths with Structs

Let Phoenix extract IDs from structs:

~p"/users/#{user}/posts/#{post}"

Returns: "/users/42/posts/17"

This uses the Phoenix.Param protocol to extract the ID.

Custom Param Implementation

Implement custom URL generation for structs:

defimpl Phoenix.Param, for: MyApp.Blog.Post do def to_param(%{slug: slug}), do: slug end

Now this generates slug-based URLs:

~p"/posts/#{post}"

Returns: "/posts/my-great-post"

Router Configuration Example

Complete Router Setup

A typical Phoenix router includes multiple pipelines and scopes:

defmodule HelloWeb.Router do use HelloWeb, :router

pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {HelloWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers end

pipeline :api do plug :accepts, ["json"] end

scope "/", HelloWeb do pipe_through :browser

get "/", PageController, :home
get "/hello", HelloController, :index
get "/hello/:messenger", HelloController, :show

end

scope "/api/v1", HelloWeb.Api.V1, as: :api_v1 do pipe_through :api

resources "/users", UserController, only: [:index, :show]

end

Admin interface

scope "/admin", HelloWeb.Admin, as: :admin do pipe_through [:browser, :admin_auth]

resources "/users", UserController
resources "/posts", PostController

end

Enable LiveDashboard in development

if Mix.env() in [:dev, :test] do import Phoenix.LiveDashboard.Router

scope "/" do
  pipe_through :browser
  live_dashboard "/dashboard", metrics: HelloWeb.Telemetry
end

end end

When to Use This Skill

Use this skill when you need to:

  • Define new routes for controllers and actions

  • Create RESTful resource routes for CRUD operations

  • Organize routes with scopes and namespaces

  • Build nested resource relationships

  • Configure request processing pipelines

  • Generate verified route paths in controllers and templates

  • Implement API versioning with scoped routes

  • Debug routing issues and inspect available routes

  • Forward requests to external plugs or applications

  • Implement custom URL slug generation

  • Set up authentication and authorization pipelines

  • Create multi-tenant routing architectures

  • Build admin interfaces with separate scopes

  • Configure different response formats (HTML, JSON, etc.)

Best Practices

  • Use verified routes - Always use ~p sigil for compile-time safety

  • Group related routes - Use scopes to organize routes logically

  • Limit resource actions - Only generate routes you actually need

  • Name scopes clearly - Use descriptive scope prefixes and aliases

  • Keep pipelines focused - Each pipeline should have a single responsibility

  • Order routes carefully - More specific routes should come before general ones

  • Use resources - Prefer resources over individual route declarations

  • Document custom routes - Add comments for non-standard routing patterns

  • Avoid deep nesting - Limit nested resources to 2-3 levels maximum

  • Version APIs - Use scopes for API versioning

  • Secure sensitive routes - Apply authentication pipelines appropriately

  • Test route resolution - Verify routes resolve to correct controllers

  • Use forward wisely - Forward to well-defined plug interfaces

  • Inspect regularly - Use mix phx.routes during development

  • Follow conventions - Stick to RESTful conventions for resources

Common Pitfalls

  • Hardcoding paths - Using strings instead of verified routes

  • Over-nesting resources - Creating deeply nested resource hierarchies

  • Missing pipeline - Forgetting to pipe routes through required pipelines

  • Wrong route order - General routes catching specific route requests

  • Exposing all actions - Generating unnecessary CRUD routes

  • Not using scopes - Repeating controller module prefixes

  • Inconsistent naming - Mixing naming conventions for routes

  • Skipping CSRF protection - Removing security plugs without understanding implications

  • Missing authentication - Not protecting sensitive routes

  • Duplicate routes - Defining the same route in multiple places

  • Incorrect HTTP verbs - Using wrong verbs for actions (GET for destructive actions)

  • Not testing routes - Failing to verify route configuration

  • Exposing internal routes - Making debug/admin routes available in production

  • Complex route logic - Putting business logic in route definitions

  • Ignoring route conflicts - Not checking for overlapping route patterns

Resources

  • Phoenix Routing Guide

  • Phoenix.Router Documentation

  • Phoenix.VerifiedRoutes Documentation

  • Plug.Router Documentation

  • Phoenix Plug Guide

  • Phoenix Request Lifecycle

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

android-jetpack-compose

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

storybook-story-writing

No summary provided by upstream source.

Repository SourceNeeds Review