Andy from Webcrunch

Subscribe for email updates:

Portrait of Andy Leverenz
Andy Leverenz

June 7, 2021

Last updated November 5, 2023

Ruby on Rails Routing

Ruby on Rails and its routing capabilities make creating new resources and URL structures a breeze. This guide is a walk-through of some use cases for routing with the framework.

Why routing?

A strength of any web application framework is to lift a lot of the load for you. Assuming there are conventions to uphold can save teams loads of time when adding and shipping new features.

Rather than craft URL schemes and invoke ways to pass in property ids you can leverage the internals of the framework to get the job done in a few lines of code or less.

Routing with Rails

There are virtually endless ways to declare new routing schemes in a Ruby on Rails application but my hope is this guide sheds some light on the basics. Be sure to leverage the documentation for the most up-to-date and accurate depictions of all that is in this article/video.

How can I view what routes my application has?

There are a couple of ways to view your existing routing.

  1. Assuming you have your rails server running locally you can visit localhost:3000/rails/info/routes. This displays a list of which you can filter through to see all the available options. As an application scales so does this list so it might be tough to navigate longer term.

  2. If you prefer the console you can also run rails routes within your project's root folder. Doing so displays the same list as the example before though again it can get tough to read as the application scales.

Root route

A great place to start is known as the "root" route. This for all things considered is your app's "homepage". Whenever someone visits your website or you boot up your local server, this is the first place the app will load.

Creating a root route requires a controller and an action on that controller to work. Additionally, to render content you would need a view template as well.

Example:

root to: "home#index"

Here I'm rooting to the home_controller.rb and the index action/method inside that controller. Rails would look for the view in app/views/home/index.html.erb to resolve the HTML/ERB that could be rendered here.

Resources (RESTful)

Ruby on Rails follows the RESTful pattern by default. Other technologies like GraphQL exist today to alter this contstruct but in the wild, I would bet most APIs are still following REST conventions.

If you're new to REST it stands for Representational state transfer. The goal of REST is to increase at scale across any size application all while being fast and efficient. This ultimately is a fancy way to explain the different forms of HTTP requests an API can hook into.

In rails, a resource stands for something like a basic object or model. When you define this it assumes you have a table in the database with a similar naming convention.

posts_table in your database might correspond to resources :posts for example.

resources :posts

Here's a basic example of the full RESTful solution condensed into one line.

resources :posts, :categories, :tags

You can comma separate multiple resources if you have many.

Taking our first example you could define it and visit localhost:3000/rails/info/routes or run rails routes and see something similar.

# GET   /posts -    posts#index -   display a list of all posts
# GET   /posts/new -    posts#new - return an HTML form for creating a new posts
# POST  /posts -    posts#create -  create a new posts
# GET   /posts/:id -    posts#show -    display a specific posts
# GET   /posts/:id/edit -   posts#edit -    return an HTML form for editing a posts
# PATCH/PUT     /posts/:id -    posts#update -  update a specific posts
# DELETE    /posts/:id -    posts#destroy - delete a specific posts

Examples:

Putting this routing to use in views and helpers

  • posts_path returns /posts
  • new_post_path returns /posts/new
  • edit_posts_path(:id) returns /posts/:id/edit
  • - (edit_post_path(10) returns posts/10/edit)
  • post_path(:id) returns /posts/:id (for instance, post_path(10) returns /posts/10)

Resource (RESTful but singular)

Sometimes, you have a resource that clients always look up without referencing an ID. So intead of /profile/1 it can be just /profile

Rails controller actions would include: show, new, create, edit, update,destroy


# GET   /profile/new - profiles#new - return an HTML form for creating the profile
# POST  /profile - profiles#create - create the new profile
# GET   /profile - profiles#show - display the one and only profile resource
# GET   /profile/edit - profiles#edit - return an HTML form for editing the profile
# PATCH/PUT     /profile - profiles#update - update the one and only profile resource
# DELETE    /profile - profiles#destroy -  delete the profile resource

Namespacing

If you want a specific path for a resource you can "contain" it inside a namespace. This is common for private or admin areas of an app or places you'd like more control over the URL schema.

namespace :admin do
  resources :articles
end

Results in

# GET   /admin/articles - admin/articles#index -    admin_articles_path
# GET   /admin/articles/new - admin/articles#new -  new_admin_article_path
# POST  /admin/articles -   admin/articles#create - admin_articles_path
# GET   /admin/articles/:id -   admin/articles#show -   admin_article_path(:id)
# GET   /admin/articles/:id/edit -  admin/articles#edit -   edit_admin_article_path(:id)
# PATCH/PUT     /admin/articles/:id - admin/articles#update -   admin_article_path(:id)
# DELETE    /admin/articles/:id - admin/articles#destroy -  admin_article_path(:id)

For even more organization you can add ruby modules to the mix to move your controller in the admin space so your routing and controllers follow the same encapsulation patterns.

scope module 'admin' do
  resources :articles
end

# or

resources :articles, module: :admin

Your controller would then move to a module or in app/controllers/admin/articles_controller.rb instead of just app/controllers/.

Inside the controller you would write the class a bit differently to compensate

Admin::ArticlesController < ApplicationController
 ...
end

Nesting routes

Simply nest like you would with a ruby block. It's recommended to never nest 3 levels deep although it's possible.

resources :categories
  resources :posts
end

Example output and URL schemes:

# GET /categories/:category_id/posts
# posts#index
# display a list of all posts for a specific category
# category_posts_path(@category) # requires an instance of Category

# ----

# GET /categories/:category_id/posts/new
# posts#new
# return an HTML form for creating a new post belonging to a specific category
# new_category_post_path(@category) # requires an instance of Category

# ----

# POST /categories/:category_id/posts
# posts#create
# create a new post belonging to a specific category

# ----

# GET /categories/:category_id/posts/:id
# posts#show
# display a specific post belonging to a specific category
# category_post_path(@category, @post) # requires an instance of Category and Post

# ----

# GET /categories/:category_id/posts/:id/edit
# posts#edit
# return an HTML form for editing an post belonging to a specific category
# edit_category_post_path(@category, @post) # requires an instance of Category and Post

# ----

# PATCH/PUT /categories/:category_id/posts/:id
# posts#update
# update a specific post belonging to a specific category

# ----

# DELETE /categories/:category_id/posts/:id
# posts#destroy
# delete a specific post belonging to a specific category
# category_post_path(@category, @post), method: :delete # requires an instance of Category and Post

Routing Concerns

Routing concerns allow you to declare common routes that can be reused inside other resources and routes.

concern :commentable do
  resources :comments
end

# or
resources :comments, concerns: :commentable # this can be an array too i.e. [:commentable, :taggable]


# the above is equivelant to
resources :messages do
  resources :comments
end

resources :articles do
  resources :comments
  resources :taggable, only: :index
end

Adding more RESTful actions

You can extend the default resources and resource to include additional restful and non-restful routing.

resources :photos do
  member do
    get 'preview'
  end
end

This will recognize /photos/1/preview with a GET HTTP request, and route to the preview action of PhotosController, with the resource id value passed in params[:id]. It will also create the preview_photo_url and preview_photo_path helpers automatically 🏄.

Adding Collection Routes

If you need a collection style of response you can add that much like the member block. Swap member with collection and you're all set.

resources :photos do
  collection do
    get 'search'
  end
end

Non-RESTful routing

A quick way to add routing for virtually any one-off path you need

get 'my/:id', to: 'users#dashboard'

returns my/1

Defaults

Only want to return JSON or some form of media? (jpg, xml, etc...)

get 'users/:id', to: 'users#show', defaults: { format: 'json' }

Here Rails would match users/1 to the show action of UsersController, and set params[:format] to json.

Naming routes

You can name a route whatever you please even if it doesn't match controller or path logic. Use the as a method to pass a symbol of the name you wish to use.

get 'exit', to: 'sessions#destroy', as: :logout

If you want to override parts of a resource you can by passing a named route first.

get ':username', to: 'users#show', as: :user
resources :users

This will define a user_path method that will be available in controllers, helpers, and views that will go to a route such as /bob. Inside the show action of UsersController, params[:username]will contain the username for the user. Change:username in the route definition if you do not want your parameter name to be :username.

Part 2 coming soon

With the basics of routing out of the way I hope I helped shed some light on how the conventional routing patterns work inside Ruby on Rails. It gets more advanced from here. If you need subdomains, bot-detection/mitigation, alternative languages, and more look for another part explaining those in more detail to come.

Be sure to subscribe to my newsletter or YouTube channel if you haven't already. This helps me keep going and bring you the best content I can!

Tags: routing rails
Link this article
Est. reading time: 9 minutes
Stats: 5,514 views

Categories

Collection

Part of the Ruby on Rails collection