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 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 opertaions, REST allows us to build an infrustructure 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.

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 wat to factor duplication out of options passed to a aseries 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 contraints to enforce subdomain

Kepping out API under its own subdomain allows load ballancing 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 do few things in our host file. Add 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 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

Thsi 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 wella s 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 elimante 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 consistancy

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

Be first to comment

Leave a Reply