//

RUBY API – REST ROUTES, CONSTRAINTS & NAMESPACES

In this article, we are going to study REST, routes, constraints, and namespaces. Before we dive into these concepts let’s take a look at how web-based services in the perspective of web users.

Let's look at an example application that uses a Web API as a service.

How does a video streaming service work?

You can create a channel say Channel A from your web application. Then you can access Channel A from your mobile app. So as you can see your Web API can provide data for all sort of different applications.

How the client & server communicate?

The communication from the client to server and back to the client of the above application can be described as follows:

Client ---> CREATE /channels video1, video2, video3 ---> Server Client <--- OK channel12 was created.------------------ Server

Client --- READ /channels/12 --------> Server Client <-- OK video1,video2,video3 --- Server

Client --- READ /channels ----------------------> Server Client <-- OK chennel10, channel11, channel12 --- Server

Client --- READ /channels/fake10 --> Server Client <-- ERROR invalid channel --- Server

Notice the pattern here: A set of commands performed on things generating responses: the very foundation of a REST API.

REST

REST stands for REpresentational State Transfer. By following a strict set of operations, REST allows us to build an infrastructure that can be supported widely with different types of applications.

Where does our API start?

A common practice for implementing our API is starting with Routes file.

config/routes.rb

resources :students

In Rails, command line let's create routes:

rake routes

This lists all the routes, URIs, Controller#actions, and URL helpers available on our app.

Controller#actions

Restricting Routes with options

We might not need all these routes, the resources method gives us options to restrict routes to optimize memory as well as speeds up the routing process.

config/routes.rb

resources :students, except: :destroy

Here we are excluding the destroy route above which has a DELETE method. To only create one route we can use only option.

config/routes.rb

resources :students, only: :index

This will make index as the only reachable action.

Restricting routes with multiple actions

Both :only and :except can also take an array of actions.

config/routes.rb

resources :students, only: [:index, :show]
resources :teachers, only: [:destroy, :edit, :update]

Using with_options on routes

The with_options method is an elegant way to factor duplication out of options passed to a series of method calls.

Take a look at the routes file below:

resources :students, only: :index
resources :teachers, only: :index
resoucres :principles, only: :index 

Let's use with_options so the options passed as arguments are automatically added to the resources.

Using constraints to enforce subdomain

Keeping out API under its own subdomain allows load balancing traffic at the DNS level, way faster than doing it at the application level.

config/routes.rb

resources :courses      # http://school.com/courses
resources :students, constraints: { subdomain: 'api' }   # http://api.school.com/students
resources :teachers, constraints: { subdomain: 'api' }   # http://api.school.com/teachers

Let's use contraints to rewrite above code:

config/routes.rb

resources :courses      

constraints subdomain: 'api' do
    resources :students
    resources :teachers
end

Network subdomain in development

Network configuration for supporting subdomains in development.

In order to develop this locally, we have done a few things in our host file. Add the following entries into your host file.

etc/hosts.rb

127.0.0.1 school-dev.com
127.0.0.1 api.school-dev.com

Now your URLs will look as follows:

http://school-dev.com:3000
http://api.school-dev.com:3000

Don't forget the port number is still required. There are other alternatives you can use:

Keeping Web and API controllers organized

It's not uncommon to use the same Rails code base for both web site and Web API.

config/routes.rb

resources :pages   # serves web site, not a web API      

constraints subdomain: 'api' do
    resources :students
end

This will render web API and the web site controllers under the same folder. It's a good practice to keep web controllers separate from API controllers. config/routes.rb

resources :pages   # serves web site, not a web API      

constraints subdomain: 'api' do
    names :api do
        resources :students
    end
end

This will result in web API controllers having their own namespace.

app/controllers/api/students_controller.rb

module Api
   class ZombiesController < ApplicationController
   end
end

Web API controllers are part of the API module.

app/controllers/pages_controller.rb

class PagesController < ApplicationController
end

Web site controllers remain on top-level namespace

Removing duplication from the URL

Enforcing both subdomain namespace as well as API namespace, creates duplication:

http://api.school.com/api

config/routes.rb

constraints subdomain: 'api' do
    namespace :api do
        resources :students
    end
end

We can eliminate the duplication by passing a path URI

config/routes.rb

constraints subdomain: 'api' do
    namespace :api, path: '/' do
        resources :students
    end
end

http://api.school.com/

We can use a short syntax by passing in the path and the constraints as options to the namespace method.

namespace :api, path: '/', constraints: { subdomain: 'api'} do
    resources :students
end

API case consistency

To make api all-caps, we need to add an acronym:

config/initializers/inflections.rb

ActiveSupport::Inflector.inflections(:en) do |inflect|
    inflect.acronym 'API'
end

Now we can write our API module in all-caps:

app/controllers/api/students_controller.rb

module API
   class ZombiesController < ApplicationController
   end
end