Advertisement
  1. Code
  2. Ruby on Rails

Membuat API Menggunakan Rails

Scroll to top
Read Time: 12 min

() translation by (you can also view the original English article)

Zaman sekarang kita sudah terbiasa dengan mengandalkan API (Application Programming Interface). Tidak hanya servis besar seperti Facebook dan Twitter yang mempekerjakan mereka, API sangat populer akibat penyebaran client-side framework seperti React, Angular, dan masih banyak lagi. Ruby juga mengikuti tren ini, dan versi terbaru memiliki fitur untuk Anda membuat API-Only applications

Pada awalnya fungsi ini dikelompokan menjadi beberapa item yang disebut rails-api, tetapi sejak Rails5 di rilis, sekarang termasuk kedalam inti dari framework. Fitur ini bersama dengan ActionCable menjadi salah satu yang ditunggu, dan sekarang kita akan mendiskusikan ini.

Artikel ini mencakup bagaimana cara membuat API Rails application dan menjelaskan bagaimana cara membuat routes dan controllers, di respon oleh JSON format, menambahkan serializers, dan menyiapkan CORS (Cross-Origin Resource Sharing). Anda juga akan belajar tentang beberapa pilihan untuk mengamankan suatu API dan melindunginya dari penyalahgunaan.

Sumber dari artikel ini ada di GitHub.

Membuat API-Only Application

Untuk memulai, jalankan perintah berikut:

1
rails new RailsApiDemo --api

Perintah diatas akan membuat API-only Rails application baru yang disebut RailsApiDemo. Jangan lupa dukungan untuk pilihan --api telah ditambahkan hanya di Rails 5, jadi pastikan Anda memiliki ini atau versi terbaru yang telah terinstall.

Buka Gemfile dan catat ini lebih kecil dari biasa: gems seperti coffee-railsturbolinks, and sass-rails telah bilang.

Config/application.rb mengandung baris baru:

1
config.api_only = true

Ini berarti Rails akan memuat kumpulan yang lebih kecil dari middleware: sebagai contoh, tidak ada cookie dan sessions yang didukung. Bahkan jika Anda mencoba untuk membuat scaffold,views dan assers itu tidak akan dibuat. Sebeneranya jika Anda melihat views/layouts directory, Anda akan melihat bahwa application.html.erb file telah menghilang juga.

Hal lain yang penting tetapi yang berbeda adalah ApplicationController diturunkan dari ActionController::API, bukan ActionController::Base.

Kurang lebih seperti itu, ini adalah dasar Rails application yang telah Anda lihat berkali-kali. Sekarang mari mulai menambahkan beberapa dari model sehingga kita dapat mengerjakan sesuatu:

1
rails g model User name:string
2
rails g model Post title:string body:text user:belongs_to
3
rails db:migrate

Bukan sesuatu yang menarik yang ada sekarang: kiriman dengan title, dan body yang dimiliki pengguna.

Pastikan suatu asosiasi yang tepat telah disiapkan dan juga mengandung beberapa validasi sederhana:

models/user.rb

1
  has_many :posts
2
3
  validates :name, presence: true

models/post.rb

1
  belongs_to :user
2
3
  validates :title, presence: true
4
  validates :body, presence: true

Menakjubkan! Tahap berikutnya adalah memuat beberapa contoh dari record menjadi table yang baru.

Memuat Demo Data

Cara termudah untuk memuat suatu data adalah dengan memanfaatkan seeds.rb file didalam db directory. Namun, aku pemalas (seperti kebanyak programmer) dan tidak mau memikirkan suatu sample content. Oleh karena itu kenapa kita tidak memanfaatkan kelebihan dari faker gem yang dapat memproduksi data acak dari berbagai: nama, emails, hipster word, "lorem ipsum" text, dan masih banyak lagi.

Gemfile

1
group :development do
2
    gem 'faker'
3
end

Install gem:

1
bundle install

Sekarang modifikasi seeds.rb:

db/seeds.rb

1
5.times do
2
  user = User.create({name: Faker::Name.name})
3
  user.posts.create({title: Faker::Book.title, body: Faker::Lorem.sentence})
4
end

Terakhir, muat data Anda:

1
rails db:seed

Menanggapi dengan JSON

Sekarang, sudah pasti, kita membutuhkan beberapa routes dan controller untuk membuat API. Itu biasa dalam membentuk suatu API's didalam api/ path. Dan juga developers biasanya menyediakan versi dari APIs di dalam sebuah path, contoh api/v1/. Pada akhirnya jika ada yang berubah dan harus diperkenalkan, Anda dengan mudah membuat namecpace(v2) dan controller yang terpisah.

Ini adalah bagaimana routes Anda terlihat:

config/routes.rb

1
namespace 'api' do
2
    namespace 'v1' do
3
      resources :posts
4
      resources :users
5
    end
6
end

Ini membuat routes seperti:

1
api_v1_posts GET    /api/v1/posts(.:format)     api/v1/posts#index
2
             POST   /api/v1/posts(.:format)     api/v1/posts#create
3
 api_v1_post GET    /api/v1/posts/:id(.:format) api/v1/posts#show

Anda juga bisa mencakup cara selain menggunakan namespace, tetapi pada dasarnya ini akan melihat pada UsersController dan PostsController didalam controllers directory, tidak didalam controllers/api/v1, jadi berhati–hatilah.

Membuat api dengan nested directory v1 didalam sebuah controllers. Bersama dengan controllers Anda:

controllers/api/v1/users_controller.rb

1
module Api
2
    module V1
3
        class UsersController < ApplicationController
4
        end
5
    end
6
end

controllers/api/v1/posts_controller.rb

1
module Api
2
    module V1
3
        class PostsController < ApplicationController
4
        end
5
    end
6
end

Perlu dicatat Anda tidak hanya memerlukan nest di controllers file dibawah api/v1 path, tetapi Anda juga membutuhkan class itu sendiri yang juga memiliki namespace didalam modul Api dan V1

Pertanyaan berikutnya adalah bagaimana cara untuk merespon secara tepat dengan JSON-formatted data? Di artikel ini kita akan mencoba solusi ini: jBuilder dan active model_serializers gems. Jadi sebelum kita lanjut ke tahap selanjutnya, letakkan mereka di Gemfile:

Gemfile

1
gem 'jbuilder', '~> 2.5'
2
gem 'active_model_serializers', '~> 0.10.0'

Kemudian jalankan:

1
bundle install

Menggunakan jBuilder Gem

jBuilder adalah suatu gem terkenal yang dibuat oleh tim Rails yang menyediakan DSL sederhana(domain specific language) yang memperbolehkan Anda untuk mendefinisi struktur JSON pada views.

Seharusnya kita menginginkan untuk melihat semua kiriman ketika pengguna menyentuh index action:

controllers/api/v1/posts_controller.rb

1
 def index
2
    @posts = Post.order('created_at DESC')
3
end

Yang Anda butuhkan adalah membuat nama view setelah melakukan aksi dengan .json.jbuilder extension. Catat bahwa view harus diletakan pada api/v1 path:

views/api/v1/posts/index.json.jbuilder

1
json.array! @posts do |post|
2
  json.id post.id
3
  json.title post.title
4
  json.body post.body
5
end

json.array! traverses the @posts array. json.idjson.title dan json.body membentuk suatu kunci dengan nama yang sesuai argument sebagai values. Jika Anda masuk http://localhost:3000/api/v1/posts.json, Anda akan melihat ini:

1
[
2
    {"id": 1, "title": "Title 1", "body": "Body 1"},
3
    {"id": 2, "title": "Title 2", "body": "Body 2"}
4
]

Bagaimana jika kita menginginkan untuk melihat penulias disetiap kiriman? Itu mudah:

1
json.array! @posts do |post|
2
  json.id post.id
3
  json.title post.title
4
  json.body post.body
5
  json.user do
6
    json.id post.user.id
7
    json.name post.user.name
8
  end
9
end

Ouput akan berubah menjadi:

1
[
2
    {"id": 1, "title": "Title 1", "body": "Body 1", "user": {"id": 1, "name": "Username"}}
3
]

Konten dari .jbuilder fadalah suatu ruby code, sehingga Anda akan menyadari semua operasi dasar seperti biasa.

Catatan bahwa jbuilder mendukung partials seperti halnya ordinary Rails view, sehingga Anda juga akan bilang:

1
json.partial! partial: 'posts/post', collection: @posts, as: :post

dan kemudian membuat views/api/v1/posts/_post.json.jbuilder file dengan konten sebagai berikut:

1
json.id post.id
2
json.title post.title
3
json.body post.body
4
json.user do
5
    json.id post.user.id
6
    json.name post.user.name
7
end

Jadi, seperti yang Anda lihat, jbuilder itu mudah dan sesuai. Sehingga, sebagai alternatif, Anda akan terus dengan serializers, jadi mari kita diskusi mereka di sesi berikutnya.

Menggunakan Serializers

rails_model_serializers gem dibuat oleh tim yang di me-manage rails-api. Sebagai dokumentasi,rails_model_serializers membawa konvesi daripada konfigurasi ke generasi JSON. Pada dasarnya, Anda akan mendefinisikan dimana Anda akan menggunakan saat serialization (JSON generation).

Ini adalah serializer pertama kita:

serializers/post_serializer.rb

1
class PostSerializer < ActiveModel::Serializer
2
  attributes :id, :title, :body
3
end

Di sini bisa kita mengatakan bahwa semua bagian ini harus hadir dalam JSON yang dihasilkan. Sekarang metode seperti to_json dan as_json dipanggil selama pada kiriman yang akan menggunakan konfigurasi dan mengembalikan konten yang benar.

Untuk melihat secara langsung, modifikasi index action seperti ini:

controllers/api/v1/posts_controller.rb

1
def index
2
    @posts = Post.order('created_at DESC')
3
    
4
    render json: @posts
5
end

as_json akan secara otomatis dipanggil pada @posts object.

Bagaimana dengan pengguna? Serializers memperbolehkan Anda untuk mengindikasi hubungan, seperti model. Yang lebih lagi, serializers bisa di nested:

serializers/post_serializer.rb

1
class PostSerializer < ActiveModel::Serializer
2
  attributes :id, :title, :body
3
  belongs_to :user
4
5
  class UserSerializer < ActiveModel::Serializer
6
    attributes :id, :name
7
  end
8
end

Sekarang ketika Anda me-serialisasi kiriman, itu akan otomatis mengandung nested user key dengan id dan nama. Jika Anda membuatnya terakhir itu akan secara membuat serializer secara terpisah untuk pengguna dengan :id attribute kecuali:

serializers/post_serializer.rb

1
class UserSerializer < ActiveModel::Serializer
2
    attributes :name
3
end

Kemudian @user.as_json tidak akan membalikan user's id. Tetap, @post.as_json akan mengembalikan username dan id, jadi perlu di ingat.

Mengamankan API

Di banyak kasus, kita tidak mau seorang pun melakukan aksi menggunakan API. Jadi mari memberikan pengecekan keamanan sederhana dan memaksa pengguna kita untuk mengirim token mereka ketika membuat atau menghapus kiriman.

Token memiliki daya hidup tak terbatas dan dibuat ketika pengguna melakukan registrasi. Pertama, tambahkan token baru pada kolom di table user:

1
rails g migration add_token_to_users token:string:index

Index ini akan menjamin keunikan karena tidak akan ada dua pengguna dengan satu token yang sama:

db/migrate/xyz_add_token_to_users.rb

1
add_index :users, :token, unique: true

Gunakan Migrasi:

1
rails db:migrate

Sekarang tambahkan callback before_save:

models/user.rb

1
before_create -> {self.token = generate_token}

Metode generate_token private akan membuat sebuah token yang siklus tak terbatas dan melihat apakah token unik atau tidak. Secepat mungkin ketika token dibuat, mengembalikan:

models/user.rb

1
private
2
3
def generate_token
4
    loop do
5
      token = SecureRandom.hex
6
      return token unless User.exists?({token: token})
7
    end
8
end

Anda akan menggunakan algoritma untuk membuat token, sebagai contoh seperti MD5 hash dari username dan beberapa salt.

Registrasi Pengguna

Tentu saja, kita juga membutuhkan pengguna untuk mendaftar, karena mereka tidak akan bisa mendapatkan token mereka jika tidak. Saya tidak mau memperkenalkan HTML views pada aplikasi, jadi mari kita menambahkan metode API:

controllers/api/v1/users_controller.rb

1
def create
2
    @user = User.new(user_params)
3
    if @user.save
4
      render status: :created
5
    else
6
      render json: @user.errors, status: :unprocessable_entity
7
    end
8
end
9
10
private
11
12
def user_params
13
    params.require(:user).permit(:name)
14
end

Ini ide bagus untuk mengembalikan HTTP status code sehingga developers mengerti apa yang terjadi. Sekarang Anda akan membat serializer baru untuk pengguna atau tetap dengan .json.jbuilder file. Saya lebih memilih variasi latter (itu kenapa saya tidak mau menggunakan :json sebagai metode untuk melakukan render), tetapi Anda bebas untuk memilih semua itu. Catatan, bahwa, token itu tidak selalu harus di serialisasi, sebagai contoh ketika Anda mengebalikan daftar dari semua pengguna, itu seharusnya bisa membuat aman!

views/api/v1/users/create.json.jbuilder

1
json.id @user.id
2
json.name @user.name
3
json.token @user.token

Tahap berikutnya adalah dengan mencoba apakah semuanya berjalan dengan lancar. Apakah Anda juga akan menggunakan curl command atau menulis beberapa kode Ruby Karena artikel ini berhubungan dengan Ruby, saya akan memilih opsi coding.

Menguji Registrasi Pengguna

Untuk melakukan HTTP request, kita akan menggunakan Faraday gem, yang menyediakan interface yang sederhana daripada kebanyak adapters (defaultnya adalah Net::HTTP). Membuat Ruby file yang terpisah, termasuk Faraday, dan mempersiapkan untuk client:

api_client.rb

1
require 'faraday'
2
3
client = Faraday.new(url: 'http://localhost:3000') do |config|
4
  config.adapter  Faraday.default_adapter
5
end
6
7
response = client.post do |req|
8
  req.url '/api/v1/users'
9
  req.headers['Content-Type'] = 'application/json'
10
  req.body = '{ "user": {"name": "test user"} }'
11
end

Semua pilihan ini dapat dijelaskan sendiri: seperti kita memilih default adapter, memperisapkan request URL untuk http://localhost:300/api/v1/users, mengubah tipe konten untuk application/json, dan menyediakan sebuah request.

Respon dari server mengandung JSON, jadi untuk meneruskan saya akan menggunakan Oj gem:

api_client.rb

1
require 'oj'
2
3
# client here...

4
5
puts Oj.load(response.body)
6
puts response.status

Berbeda dengan memecah respon, saya juga memperlihatkan status code yang bermaksud untuk debugging.

Sekarang Anda bisa menjalakan script ini:

1
ruby api_client.rb

dan menyimpan token yang diterima di suatu tempat, kita akan menggunakan ini di sesi berikutnya.

Autentikasi dengan Token

Untuk memastikan autentikasi token, metode authenticate_or_request_with_http_token bisa digunakan. Ini termasuk bagian dari ActionController::HttpAuthentication::Token::ControllerMethods modul, jadi harus diingat ini termasuk:

controllers/api/v1/posts_controller.rb

1
class PostsController < ApplicationController
2
    include ActionController::HttpAuthentication::Token::ControllerMethods
3
    # ...
4
end

Tambahkan before_action dan metode yang sesuai:

controllers/api/v1/posts_controller.rb

1
before_action :authenticate, only: [:create, :destroy]
2
3
# ...
4
5
private
6
7
# ...
8
9
def authenticate
10
    authenticate_or_request_with_http_token do |token, options|
11
      @user = User.find_by(token: token)
12
    end
13
end

Sekarang jika token tidak disiapkan atau pengguna dengan token tidak dapat ditemukan, sebuah 401 error akan ditampilkan, Menahan suatu aksi untuk mengeksekusi.

Perlu dicatat bahwa komunikasi antara client dan server harus dibuat melalui HTTPS, jika tidak token bisa dengan mudah dipalsukan. Tentu saja, ini bukanlah solusi yang ideal, dan di banyak kasus lebih baik untuk menggunakan OAuth 2 protocol untuk autentikasi. Paling tidak, ada dua gems yang bisa dengan mudah mendukung proses dari fitur ini: Doorkeeper dan oPRO.

Membuat Posting

Untuk melihat autentikasi kita yang dijalankan, tambahkan untuk membuat suatu PostsController:

controllers/api/v1/posts_controller.rb

1
def create
2
    @post = @user.posts.new(post_params)
3
    if @post.save
4
        render json: @post, status: :created
5
    else
6
        render json: @post.errors, status: :unprocessable_entity
7
    end
8
end

Kita bisa mengambil keuntungan dari serializer disini untuk melihat JSON yang tepat. @user sudah siap didalam before_action.

Sekarang coba semuanya menggunakan kode yang sederhana:

api_client.rb

1
client = Faraday.new(url: 'http://localhost:3000') do |config|
2
  config.adapter  Faraday.default_adapter
3
  config.token_auth('127a74dbec6f156401b236d6cb32db0d')
4
end
5
6
response = client.post do |req|
7
  req.url '/api/v1/posts'
8
  req.headers['Content-Type'] = 'application/json'
9
  req.body = '{ "post": {"title": "Title", "body": "Text"} }'
10
end

Ubah argument yang dipakai oleh token_auth dengan token yang diterima pada saat registrasi, dan menjalankan script.

1
ruby api_client.rb

Menghapus Posting

Menghapus sebuah post dilakukan dengan cara yang sama. Menambahkan action destroy :

controllers/api/v1/posts_controller.rb

1
def destroy
2
    @post = @user.posts.find_by(params[:id])
3
    if @post
4
      @post.destroy
5
    else
6
      render json: {post: "not found"}, status: :not_found
7
    end
8
end

Kita hanya memperbolehkan pengguna untuk menghapus kiriman yang hanya miliknya Jika kiriman terhapus dengan benar, 204 status code (tidak ada konten) akan di tampilkan. Ini adalah potongan dari kode yang akan dicoba dengan fitur baru ini:

api_client.rb

Gantilah id kiriman dengan angka yang Anda mau.

1
response = client.delete do |req|
2
  req.url '/api/v1/posts/6'
3
  req.headers['Content-Type'] = 'application/json'
4
end

Pengaturan CORS

Jika Anda mau menyediakan web service lain untuk mengakses API Anda (dari client-side), kemudian CORS (Cross-Origin Resource Sharing)bisa menyiapkannya.

Pada dasarnya, CORS memperbolehkan suatu web application untuk mengirim AJAX request ke third-party services. Pada dasarnya, CORS memperbolehkan suatu web application untuk mengirim AJAX request ke third-party services. Beruntungnya, ada gem yang disebut rack-cors yang disiapkan sehingga daoat mempermudah kita. Tambahkan ini pada Gemfile:

Gemfile

1
gem 'rack-cors'

Install ini:

1
bundle install

Dan kemudian sediakan konfigurasi didalam config/initializers/cors.rbfile. Sebenarnya, file ini sudah dibuat untuk Anda dan mengandung contoh penggunaan. Anda juga bisa menemukan contoh dokumentasi yang lengkap pada gem's page.

Konfigurasi berikut ini, sebagai contoh, akan memperbolehkan setiap orang untuk mengakses API Anda menggunakan metode:

config/initializers/cors.rb

1
Rails.application.config.middleware.insert_before 0, Rack::Cors do
2
  allow do
3
    origins '*'
4
5
    resource '/api/*',
6
      headers: :any,
7
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
8
  end
9
end

Mencegah Penyalahgunaan

Hal terakhir yang saya akan bahas di pedoman ini adalah bagaimana cara melindungi API Anda dari penyalahgunaan dan penolakkan serangan layanan. Ada gem bagus yang disebut rack-attack (dibuat oleh orang dari Kickstarter) yang memperbolehkan Anda untuk mem-blacklist atau whitelist client, mencegah server untuk melebihi request dan masih banyak lagi.

Letakkan ini pada Gemfile:

Gemfile

1
gem 'rack-attack'

Install ini:

1
bundle install

Dan kita dapat melakukan konfigurasi didalam rack_attack.rb initializer file. Dokumentasi gem menampilkan semua pengaturan yang ada dan bagaimana saran untuk pemakaian. Ini adalah contohnya mengkonfigurasi utntuk membatasi seseorang kecuali kamu untuk mengakses service dan membatasi jumlah request sebanyak 5 request per detik:

config/initializers/rack_attack.rb

1
class Rack::Attack
2
  safelist('allow from localhost') do |req|
3
    # Requests are allowed if the return value is truthy
4
    '127.0.0.1' == req.ip || '::1' == req.ip
5
  end
6
7
  throttle('req/ip', :limit => 5, :period => 1.second) do |req|
8
    req.ip
9
  end
10
end

Hal lain yang perlu dilakukan adalah memasukan RackAttack sebagai middleware:

config/application.rb

1
config.middleware.use Rack::Attack

Kesimpulan

Kita sudah sampai pada akhir artikel ini. Semoga, sekarang Anda lebih merasa percaya diri untuk membuat API dengan Rails! Catatan bahwa ini bukanlah satu-satunya cara, cara lain yang terkenal yang sudah ada sejak lama adalah Grape framework, mungkin Anda tertarik untuk melihat itu juga.

Jangan ragu-ragu untuk mengirimkan pertanyaan Anda jika ada sesuatu yang tidak jelas untuk Anda. Saya mengucapkan terimakasih telah bersama dengan Saya, dan happy coding!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.