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:
- Create models for:
- Region
- Province
- City
- Barangay
- 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.