Skip to main content Link Search Menu Expand Document (external link)

Web API Design

In the previous page, we retrieve and saved the records from a web api server

In this topic, we will design our own api similar with the PSGC

Go to app/controllers directory, create the api directory, this will be the home of all future API controllers.

Every API will eventually need to make an upgrade. We should presume that once our API is in use, someone is already using it. Avoid changing your APIs, except when errors need to be fixed.

We may plan for the future by using versions. Create a directory called v1 in the app/controller/api directory;v1is for version one and in this directory, we will add our current controllers. In the future, when we ever need to upgrade our API, we can generate a v2 directory.

Here are the endpoints that are we going to do.

  • /api/v1/regions
  • /api/v1/regions/:id
  • /api/v1/regions/:id/provinces/
  • /api/v1/provinces/
  • /api/v1/provinces/:id
  • /api/v1/provinces/:id/cities
  • /api/v1/cities/
  • /api/v1/cities/:id/
  • /api/v1/cities/:id/barangays/
  • /api/v1/barangays/
  • /api/v1/barangays/:id/

Regions

In the app/controllers/api/v1, create the regions_controller.rb file. add the index and show actions.

# app/controllers/api/v1/regions_controller.rb

class Api::V1::RegionsController < ApplicationController

  def index
    regions = Address::Region.all
    render json: regions
  end

  def show
    region = Address::Region.find(params[:id])
    render json: region
  end
end

In the controller, we will only need to have two actions in this controller. The actions retrieve all the region records from our database and return the data in JSON format.

Go to the config/routes.rb directory. Add the resources inside the api and v1 namespaces

# config/routes.rb

Rails.application.routes.draw do
  # ---

+ namespace :api do
+   namespace :v1 do
+     resources :regions, only: %i[index show], defaults: { format: :json }
+   end
+ end
end

namespace will prefix the URL path for the specified resources, and try to locate the controller under a module named in the same manner as the namespace.

HelperHTTP VerbPathController#Action
api_v1_regions_pathGET/api/v1/regions(.:format)api/v1/regions#index {:format=>:json}
api_v1_region_pathGET/api/v1/regions/:id(.:format)api/v1/regions#show {:format=>:json}

Region Endpoints

http://localhost:3000/api/v1/regions

[
    {
      "id": 1,
      "code": "010000000",
      "name": "Region I",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    {
      "id": 2,
      "code": "020000000",
      "name": "Region II",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    }, 
    # ...
]

http://localhost:3000/api/v1/regions/1

    {
      "id": 1,
      "code": "010000000",
      "name": "Region I",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    }

Province

In the app/controllers/api/v1, create the provinces_controller.rb file. Similar with the region controller, we only need the index and show actions.

# app/controllers/api/v1/provinces_controller.rb

class Api::V1::ProvincesController < ApplicationController

  def index
    region = Address::Region.find_by_id(params[:region_id])
    provinces = if region
                  region.provinces
                else
                  Address::Province.all
                end

    render json: provinces
  end

  def show
    province = Address::Province.find_by_id(params[:id])
    render json: province
  end
end

We determine whether a region is present and return all of its provinces in the index action. And if there isn’t a region, we simply return all the Philippine provinces. The show action uses the parameters id to return the specific province. Both actions return data in JSON format, exactly as the regions_controller does.

Go to the config/routes.rb directory. and add those routes.

Rails.application.routes.draw do
  # ---

  namespace :api do
    namespace :v1 do
-     resources :regions, only: %i[index show], defaults: { format: :json }
+     resources :regions, only: %i[index show], defaults: { format: :json } do
+       resources :provinces, only: :index, defaults: { format: :json }
+     end
+    
+     resources :provinces, only: %i[index show], defaults: { format: :json }
    end
  end
end
HelperHTTP VerbPathController#Action
api_v1_region_provinces_pathGET/api/v1/regions/:region_id/provinces(.:format)api/v1/provinces#index {:format=>:json}
api_v1_provinces_pathGET/api/v1/provinces(.:format)api/v1/provinces#index {:format=>:json}
api_v1_province_pathGET/api/v1/provinces/:id(.:format)api/v1/provinces#show {:format=>:json}

Province Endpoints

http://localhost:3000/api/v1/regions/1/provinces

[
    {
      "id": 1,
      "region_id": 1,
      "code": "012800000",
      "name": "Ilocos Norte",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    {
      "id": 2,
      "region_id": 1,
      "code": "012900000",
      "name": "Ilocos Sur",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    #...
  ]

http://localhost:3000/api/v1/provinces

[
    {
      "id": 1,
      "region_id": 1,
      "code": "012800000",
      "name": "Ilocos Norte",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    {
      "id": 2,
      "region_id": 1,
      "code": "012900000",
      "name": "Ilocos Sur",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    #...
  ]

http://localhost:3000/api/v1/provinces/1

{
  "id": 1,
  "region_id": 1,
  "code": "012800000",
  "name": "Ilocos Norte",
  "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
  "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}

City

In the app/controllers/api/v1, create the cities_controller.rb file.

# app/controllers/api/v1/cities_controller.rb

class Api::V1::CitiesController < ApplicationController

  def index
    province = Address::Province.find_by_id(params[:province_id])
    cities = if province
               province.cities
             else
               Address::City.all
             end

    render json: cities
  end

  def show
    city = Address::City.find(params[:id])
    render json: city
  end
end

This is similar to the province controller. This controller checks whether a province is present before determining whether to return all cities or just the cities found within it.

Go to the config/routes.rb directory.

Rails.application.routes.draw do
  # ---

  namespace :api do
    namespace :v1 do
      resources :regions, only: %i[index show], defaults: { format: :json } do
        resources :provinces, only: :index, defaults: { format: :json }
      end

-     resources :provinces, only: %i[index show], defaults: { format: :json }
+     resources :provinces, only: %i[index show], defaults: { format: :json } do
+       resources :cities, only: :index, defaults: { format: :json }
+     end
+
+     resources :cities, only: %i[index show], defaults: { format: :json }
    end
  end
end
HelperHTTP VerbPathController#Action
api_v1_province_cities_pathGET/api/v1/provinces/:province_id/cities(.:format)api/v1/cities#index {:format=>:json}
api_v1_cities_pathGET/api/v1/cities(.:format)api/v1/cities#index {:format=>:json}
api_v1_city_pathGET/api/v1/cities/:id(.:format)api/v1/cities#show {:format=>:json}

City Endpoints

http://localhost:3000/api/v1/provinces/10/cities

[
  {
    "id": 219,
    "province_id": 10,
    "code": "030801000",
    "name": "Abucay",
    "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
    "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
  },
  {
    "id": 220,
    "province_id": 10,
    "code": "030802000",
    "name": "Bagac",
    "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
    "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
  },
]

http://localhost:3000/api/v1/cities/

[
  {
    "id": 1,
    "province_id": 1,
    "code": "012801000",
    "name": "Adams",
    "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
    "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
  },
  {
    "id": 2,
    "province_id": 1,
    "code": "012802000",
    "name": "Bacarra",
    "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
    "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
  },
]

http://localhost:3000/api/v1/cities/100

{
  "id": 100,
  "province_id": 4,
  "code": "015523000",
  "name": "Mabini",
  "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
  "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}

Barangay

The endpoints and lowest geographic hierarchy of the Philippines are covered in this final section. In the app/controllers/api/v1, create the provinces_controller.rb file.

# app/controllers/api/v1/barangays_controller.rb

class Api::V1::BarangaysController < ApplicationController

  def index
    city = Address::City.find_by_id(params[:city_id])
    barangays = if city 
                  city.barangays
                else
                  Address::Barangay.all
                end
    render json: barangays
  end
  
  def show
    barangay = Address::Barangay.find(params[:id])
    render json: barangay
  end
end

Go to the config/routes.rb directory.

Rails.application.routes.draw do
  # ...
  
  namespace :api do
    resources :regions, only: %i[index show], defaults: { format: :json } do
      resources :provinces, only: :index, defaults: { format: :json }
    end

    resources :provinces, only: %i[index show], defaults: { format: :json } do
      resources :cities, only: :index, defaults: { format: :json }
    end
    
-   resources :cities, only: %i[index show], defaults: { format: :json } do
+   resources :cities, only: %i[index show], defaults: { format: :json } do
+     resources :barangays, only: :index, defaults: { format: :json }
+   end
+
+   resources :barangays, only: %i[index show], defaults: { format: :json }
  end
end
HelperHTTP VerbPathController#Action
api_v1_city_barangays_pathGET/api/v1/cities/:city_id/barangays(.:format)api/v1/barangays#index {:format=>:json}
api_v1_barangays_pathGET/api/v1/barangays(.:format)api/v1/barangays#index {:format=>:json}
api_v1_barangay_pathGET/api/v1/barangays/:id(.:format)api/v1/barangays#show {:format=>:json}

Barangay Endpoints

http://localhost:3000/api/v1/cities/100/barangays

[
    {
      "id": 2444,
      "city_id": 100,
      "code": "015523002",
      "name": "Bacnit",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    {
      "id": 2445,
      "city_id": 100,
      "code": "015523003",
      "name": "Barlo",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    # ...
]

http://localhost:3000/api/v1/barangays

[
    {
      "id": 1,
      "city_id": 1,
      "code": "012801001",
      "name": "Adams (Pob.)",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
    {
      "id": 2,
      "city_id": 2,
      "code": "012802001",
      "name": "Bani",
      "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
      "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
    },
]

http://localhost:3000/api/v1/barangays/86

{
    "id": 86,
    "city_id": 4,
    "code": "012804011",
    "name": "Payac",
    "created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
    "updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}

Back to top

Copyright © 2020-2022 Secure Smarter Service, Inc. This site is powered by KodaCamp.