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

Integrate Third-Party API

In the introduction, we demonstrate how to use rest client and retrieve records from a Web API endpoint

We will use a different Web API on this page; previously, we only retrieved the data; now, we will also save those entries.

This Web API is free, and all data is from by Philippine Standard Geographic Code (PSGC).

https://psgc.gitlab.io/api/

The Philippine Standard Geographic Code (PSGC) is a systematic classification and coding of geographic areas of the Philippines. It is based on the four well-established hierarchical levels of geographical-political subdivisions of the country, such as the administrative region, the province, the municipality/city and the barangay.

On this page we will do the following tasks:

  1. Create models for:
    • Region
    • Province
    • City
    • Barangay
  2. Create a service that will retrieve and save those records.

Reminder:

Make sure that your containers are up and running.

First, Let’s create the migration for those models.

Set Up Models and build a Service

Region Model

 $~/KodaCamp> docker-compose exec app bash
 root@0122:/usr/src/app# rails g model address::region                                                                                               
       invoke  active_record
       create  db/migrate/xxxxxxxxxxxx_create_address_regions.rb
       create  app/models/address/region.rb
# db/migrate/xxxxxxxxxxxxxx_create_address_regions.rb

class CreateAddressRegions < ActiveRecord::Migration[7.0]
  def change
    create_table :address_regions do |t| 
+     t.string :code
+     t.string :name
      t.timestamps
    end
  end
end

In app/models/address/region.rb add validation for name and code.

# app/models/address/region.rb

class Address::Region < ApplicationRecord 
  validates :name, presence: true
  validates :code, uniqueness: true
end

Next run rails db:migrate

 root@0122:/usr/src/app# rails db:migrate
   == xxxxxxxxxxxxxx CreateAddressRegion: migrating =================================
   -- create_table(:address_region)
      -> 0.0142s
   == xxxxxxxxxxxxxx CreateAddressRegion: migrated (0.0143s) ========================

After creating address_region, now we need to build service and get the data from api.

Service is a ruby object that makes it easier to separate the business logic from models or controllers.

For example, calling a web API since this logic does not belong to a model or a controller, This can be included in a service object.

We will create a service to request the data from the Web API and save the records in our database.

In the app directory, create a new file in the app/services directory. We can create a file called ph_location_service.rb.

# app/services/ph_location_service.rb

class PhLocationService 
  attr_reader :url

  def initialize
    @url = 'https://psgc.gitlab.io/api'
  end
end

So, the PhLocationService class has an instance variable @url with a value of https://psgc.gitlab.io/api, and it has an attribute reader that allows external access to this value. The initialize method sets the initial value of @url when an object is created from the class.

Regions

The Region is the first level of the geographic hierarchy in the Philippines. This endpoint returns all regions of the Philippines.

https://psgc.gitlab.io/api/regions

See an example of the endpoint’s data below;

[
  {"code":"010000000","name":"Ilocos Region","regionName":"Region I","islandGroupCode":"luzon","psgc10DigitCode":"0100000000"},
  {"code":"020000000","name":"Cagayan Valley","regionName":"Region II","islandGroupCode":"luzon","psgc10DigitCode":"0200000000"},
  {"code":"030000000","name":"Central Luzon","regionName":"Region III","islandGroupCode":"luzon","psgc10DigitCode":"0300000000"},
  {"code":"040000000","name":"CALABARZON","regionName":"Region IV-A","islandGroupCode":"luzon","psgc10DigitCode":"0400000000"},
  #  ...
]

In this data, we will use the regionName and code for our region model.

In the service, add the fetch_regions method.

class PhLocationService
  attr_reader :url

  def initialize
    @url = 'https://psgc.gitlab.io/api'
  end

+ def fetch_region
+   request = RestClient.get("#{url}/regions/")
+   data = JSON.parse(request.body)
+   data.each do |region|
+     address_region = Address::Region.find_or_initialize_by(code: region['code'])
+     address_region.name = region['regionName']
+     address_region.save
+   end
+ end
end

In this code we use rest-client gem to fetch the regions from the Web API and parse the JSON string to use it as a ruby object.

We iterate the array and save each record.

Read Carefully:

We use find_or_initialize_by to prevent duplicate records.

Always remember to call reload! everytime you change the file that you are running.

Open your Rails console, and run the new method in your service.

 root@0122:/usr/src/app# rails c
  Loading development environment (Rails 7.0.4)
  irb(main):001:0> service = PhLocationService.new
  => #<PhLocationService:0x00007fc7c026fab8 @url="https://psgc.gitlab.io/api">
  irb(main):002:0> service.fetch_regions

There are currently 17 regions in the Philippines, according to the PSGC (as of writing). It means that our region table will likely have 17 records.

Count the number of records you saved.

irb(main):001:0> Address::Region.count
=> 17

We successfully get all the records from the endpoint.

Province Model

 root@0122:/usr/src/app# rails g model address::province                                                                                               
       invoke  active_record
       create  db/migrate/xxxxxxxxxxxx_create_address_provinces.rb
       create  app/models/address/province.rb
# db/migrate/xxxxxxxxxxxxxx_create_address_provinces.rb

class CreateAddressProvinces < ActiveRecord::Migration[7.0]
  def change
    create_table :address_provinces do |t|
+     t.belongs_to :region
+     t.string :code
+     t.string :name
      t.timestamps
    end
  end
end

In app/models/address/province.rb add validation for name and code, and add association one-to-many which means region has many provinces and province belongs to region.

# app/models/address/province.rb

class Address::Province < ApplicationRecord
  validates :name, presence: true
  validates :code, uniqueness: true

  belongs_to :region
end
# app/models/address/region.rb

class Address::Region < ApplicationRecord
  validates :name, presence: true
  validates :code, uniqueness: true

+ has_many :provinces
end

Next run rails db:migrate

 root@0122:/usr/src/app# rails db:migrate
   == xxxxxxxxxxxxxx CreateAddressProvince: migrating =================================
   -- create_table(:address_province)
      -> 0.0142s
   == xxxxxxxxxxxxxx CreateAddressProvince: migrated (0.0143s) ========================

Provinces

The Province is the second level of the geographic hierarchy in the philippines. This endpoint returns all the provinces in the philippines.

https://psgc.gitlab.io/api/provinces

Here is the sample data from the endpoint we will be using the data from the name, code and regionCode.

[
  {"code":"012800000","name":"Ilocos Norte","regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0102800000"},
  {"code":"012900000","name":"Ilocos Sur","regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0102900000"},
  {"code":"013300000","name":"La Union","regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0103300000"},
  {"code":"015500000","name":"Pangasinan","regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0105500000"},
  #  ...
]

In the service, add the fetch_provinces method.

class PhLocationService
  # ---
+ def fetch_provinces
+   request = RestClient.get("#{url}/provinces")
+   data = JSON.parse(request.body)
+   data.each do |province|
+     region = Address::Region.find_by_code(province['regionCode'])
+     address_province = Address::Province.find_or_initialize_by(code: province['code'])
+     address_province.name = province['name']
+     address_province.region = region
+     address_province.save
+   end
+ end
end

We use the regionCode to find which region a province belongs to.

In rails console, run the fetch_provinces method.

irb(main):001:0> service = PhLocationService.new
=> #<PhLocationService:0x00007fc7c026fab8 @url="https://psgc.gitlab.io/api">
irb(main):002:0> service.fetch_provinces

According to the PCGC (as of writing), the total number of provinces in the philippines is 81.

Like before, Count the number of records you saved.

irb(main):001:0> Address::Province.count
=> 81

We already successfully get all provinces from the philippines.

In the philippines, the regions are made up of provinces, but there is one that doesn’t have any provinces.

The NCR doesn’t have any provinces and only have districts. We will use the province model to save the district records since they are on the same level.

This endpoint will return all the districts from the NCR region.

https://psgc.gitlab.io/api/districts

This endpoint will return all the districts from the NCR region. The data of this endpoint only have four records.

[
  {"code":"133900000","name":"First District","regionCode":"130000000","islandGroupCode":"luzon","psgc10DigitCode":""},
  {"code":"137400000","name":"Second District","regionCode":"130000000","islandGroupCode":"luzon","psgc10DigitCode":""},
  {"code":"137500000","name":"Third District","regionCode":"130000000","islandGroupCode":"luzon","psgc10DigitCode":""},
  {"code":"137600000","name":"Fourth District","regionCode":"130000000","islandGroupCode":"luzon","psgc10DigitCode":""}
]

In the service, add the fetch_districts method.

class PhLocationService
  # ---

+ def fetch_districts
+   request = RestClient.get("#{url}/districts/")
+   data = JSON.parse(request.body)
+   data.each do |district|
+     region = Address::Region.find_by(code: district['regionCode'])
+     address_district = Address::Province.find_or_initialize_by(code: district['code'])
+     address_district.name = district['name']
+     address_district.region = region
+     address_district.save
+   end
+ end
end

Go back to your Rails console, and run the fetch_districts method in your service.

irb(main):001:0> service = PhLocationService.new
=> #<PhLocationService:0x00007fc7c026fab8 @url="https://psgc.gitlab.io/api">
irb(main):002:0> service.fetch_districts

According to the PCGC (as of writing), the total number of districts in the philippines is 4. This means our total record must be 85.

Count the number of records you saved and compare it to the expected number of records.

irb(main):001:0> Address::Province.count
=> 85

Now we already finish the records for the province model.

City Model

 root@0122:/usr/src/app# rails g model address::city                                                                                               
       invoke  active_record
       create  db/migrate/xxxxxxxxxxxx_create_address_cities.rb
       create  app/models/address/city.rb
# db/migrate/xxxxxxxxxxxxxx_create_address_cities.rb

class CreateAddressCities < ActiveRecord::Migration[7.0]
  def change
    create_table :address_cities do |t| 
+     t.belongs_to :province
+     t.string :code
+     t.string :name
      t.timestamps
    end
  end
end

In app/models/address/city.rb add validation for name and code, and add association one-to-many which means province has many cities and city belongs to province.

# app/models/address/city.rb

class Address::City < ApplicationRecord
  validates :name, presence: true
  validates :code, uniqueness: true

  belongs_to :province
end
# app/models/address/province.rb

class Address::Province < ApplicationRecord 
  validates :name, presence: true
  validates :code, uniqueness: true

  belongs_to :region
+ has_many :cities
end

Next run rails db:migrate

 root@0122:/usr/src/app# rails db:migrate
   == xxxxxxxxxxxxxx CreateAddressCity: migrating =================================
   -- create_table(:address_city)
      -> 0.0142s
   == xxxxxxxxxxxxxx CreateAddressCity: migrated (0.0143s) ========================

Cities

The City is the third level of the geographic hierarchy in the philippines. This endpoint will return all the cities and municipalities of the philippines.

https://psgc.gitlab.io/api/cities-municipalities
[
   {"code":"012801000","name":"Adams","oldName":"","isCapital":false,"isCity":false,"isMunicipality":true,"provinceCode":"012800000","districtCode":false,"regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0102801000"},
   {"code":"012802000","name":"Bacarra","oldName":"","isCapital":false,"isCity":false,"isMunicipality":true,"provinceCode":"012800000","districtCode":false,"regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0102802000"},
   {"code":"012803000","name":"Badoc","oldName":"","isCapital":false,"isCity":false,"isMunicipality":true,"provinceCode":"012800000","districtCode":false,"regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0102803000"},
   {"code":"012804000","name":"Bangui","oldName":"","isCapital":false,"isCity":false,"isMunicipality":true,"provinceCode":"012800000","districtCode":false,"regionCode":"010000000","islandGroupCode":"luzon","psgc10DigitCode":"0102804000"}
]

In the service, add the fetch_cities method.

class PhLocationService
  # ---

+ def fetch_cities
+   request = RestClient.get("#{url}/cities-municipalities/")
+   data = JSON.parse(request.body)
+   data.each do |city|
+     address_city = Address::City.find_or_initialize_by(code: city['code'])
+     address_city.name = city['name']
+     address_city.province = if city['districtCode']
+                               Address::Province.find_by_code(city['districtCode'])
+                             elsif city['provinceCode']
+                               Address::Province.find_by_code(city['provinceCode'])
+                             end
+     address_city.save
+   end
+ end
end

In the code, for every loop a city could belong to a district or province. We add a conditional statement to check if we should use the district code or the province code to find the province it belongs to.

irb(main):001:0> service = PhLocationService.new
=> #<PhLocationService:0x00007fc7c026fab8 @url="https://psgc.gitlab.io/api">
irb(main):002:0> service.fetch_cities

According to the PCGC (as of writing), the Philippines has 1489 municipalities and 145 cities. Our total record must be 1634.

Count the number of records you saved.

irb(main):001:0> Address::City.count
=> 1634

The number of our records is already matches with our expectations.

Barangay Model

 root@0122:/usr/src/app# rails g model address::barangay                                                                                              
       invoke  active_record
       create  db/migrate/xxxxxxxxxxxx_create_address_barangays.rb
       create  app/models/address/barangay.rb
# db/migrate/xxxxxxxxxxxxxx_create_address_barangays.rb

class CreateAddressBarangays < ActiveRecord::Migration[7.0]
  def change
    create_table :address_barangays do |t| 
+     t.belongs_to :city       
+     t.string :code
+     t.string :name
      t.timestamps
    end
  end
end

In app/models/address/barangay.rb add validation for name and code, and add association one-to-many which means city has many barangays and barangay belongs to city.

# app/models/address/barangay.rb

class Address::Barangay < ApplicationRecord
  validates :name, presence: true
  validates :code, uniqueness: true

  belongs_to :city
end
# app/models/address/city.rb

class Address::City < ApplicationRecord
  validates :name, presence: true
  validates :code, uniqueness: true

  belongs_to :provinces 
+ has_many :barangays  
end

Barangays

The Barangay is the lowest level of the geographic hierarchy in the philippines. This endpoint will return all the barangays of the philippines.

https://psgc.gitlab.io/api/barangays

See an example of the endpoint’s data below;

[
  { "code": "012801001","name": "Adams (Pob.)","oldName": "","subMunicipalityCode": false,"cityCode": false,"municipalityCode": "012801000","districtCode": false,"provinceCode": "012800000","regionCode": "010000000","islandGroupCode": "luzon","psgc10DigitCode": "0102801001" },
  { "code": "012802001","name": "Bani","oldName": "","subMunicipalityCode": false,"cityCode": false,"municipalityCode": "012802000","districtCode": false,"provinceCode": "012800000","regionCode": "010000000", "islandGroupCode": "luzon", "psgc10DigitCode": "0102802001" },
  { "code": "012802002","name": "Buyon","oldName": "","subMunicipalityCode": false,"cityCode": false,"municipalityCode": "012802000","districtCode": false,"provinceCode": "012800000","regionCode": "010000000", "islandGroupCode": "luzon", "psgc10DigitCode": "0102802002"},
  { "code": "012802003","name": "Cabaruan", "oldName": "","subMunicipalityCode": false,"cityCode": false,"municipalityCode": "012802000", "districtCode": false, "provinceCode": "012800000", "regionCode": "010000000", "islandGroupCode": "luzon","psgc10DigitCode": "010280200" }
]

In the service, add the fetch_barangays method.

class PhLocationService
  # ---

+ def fetch_barangays
+   request = RestClient.get("#{url}/barangays/")
+   data = JSON.parse(request.body)
+   data.each do |barangay|
+     city_code = barangay['cityCode'] ? barangay['cityCode'] : barangay['municipalityCode']
+     address_barangay = Address::Barangay.find_or_initialize_by(code: barangay['code'])
+     address_barangay.name = barangay['name']
+     address_barangay.city = Address::City.find_by(code: city_code)
+     address_barangay.save
+   end
+ end
end

In your Rails console, run the fetch_barangays method. This will take a long time to complete

irb(main):001:0> service = PhLocationService.new
=> #<PhLocationService:0x00007fc7c026fab8 @url="https://psgc.gitlab.io/api">
irb(main):002:0> service.fetch_barangays

According to the PCGC (as of writing), the Philippines has 42029 barangays. Our total record must be 42029.

irb(main):001:0> Address::Barangay.count
=> 42029

Now we already retrieve the data and manage to save this in our database.


Back to top

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