Belajar Ruby on Rails - API Only & Rails API
Episode 15 of 27

Belajar Ruby on Rails - API Only & Rails API

Membedah API dengan Rails: rails new --api untuk aplikasi tanpa view, serialization dengan jbuilder, versioning API, autentikasi token untuk SPA & mobile, dan praktik membangun JSON API

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 14 kalian membangun realtime, episode 15 membahas sisi Rails yang memisahkan frontend dari backend: API-only. Tidak semua aplikasi memakai view Rails — SPA dengan React/Vue, aplikasi mobile, dan integrasi pihak ketiga butuh JSON API yang bersih dan ter-versioned.

Mengapa episode ini penting? Karena API adalah bahasa publik aplikasi kalian. Sekali dipakai konsumen (aplikasi mobile, partner), breaking change bisa sangat mahal. Episode ini mengajarkan cara membangun Rails API dengan benar sejak awal: struktur API-only, serialization yang terkendali, versioning, dan autentikasi token.

rails new --api

API-only Rails menghilangkan lapisan view dan browser:

Buat API project
rails new blog_api --api --database=postgresql
cd blog_api

Opsi --api menghasilkan project yang: tidak memuat Asset Pipeline, tidak memuat view/helper defaults, menggunakan middleware API (JSON parsing, tanpa session cookies), dan controller mewarisi ActionController::API. Hasilnya: aplikasi ringan yang hanya melayani JSON.

Note

--api bukan berarti "tidak bisa menambah view nanti". Kalian bisa menambahkan full Rails ke project API kapan pun. Pilih --api saat frontend memang terpisah (SPA, mobile) — bukan karena menganggap view itu buruk.

Serialization dengan jbuilder

Controller API tidak me-render ERB, melainkan JSON. Cara default Rails: template jbuilder — file dengan ekstensi .json.jbuilder:

RubyPostsController#index
def index
  @posts = Post.published.includes(:user).limit(20)
end
HTMLapp/views/posts/index.json.jbuilder
json.array! @posts do |post|
  json.extract! post, :id, :title, :created_at
  json.author post.user.name
  json.comments_count post.comments.size
end

Kelebihan jbuilder: kontrol penuh atas bentuk JSON — kalian memilih field mana yang diekspos (jangan sampai password_digest bocor). Controller cukup membuat instance variable; Rails otomatis memilih template .json.jbuilder sesuai format response.

Versioning API

API yang dipakai publik harus di-version. Standar: versi di URL path (/api/v1/...). Rails menyediakan namespace yang cocok untuk ini:

Rubyconfig/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :posts, only: %i[index show]
    end
  end
end

Menghasilkan GET /api/v1/postsApi::V1::PostsController. Struktur folder:

Struktur controller versioned
app/controllers/api/v1/posts_controller.rb
RubyApi::V1::PostsController
module Api
  module V1
    class PostsController < ApplicationController
      def index
        @posts = Post.published.includes(:user).limit(20)
        render json: { posts: @posts.map { |p| PostSerializer.new(p).call } }
      end
    end
  end
end

Aturan emas versioning: versi lama tetap berjalan. Saat menambah breaking change, buat v2 dan biarkan v1 hidup sampai konsumen bermigrasi. Inilah alasan versioning di path dipakai hampir semua API publik.

Autentikasi Token untuk SPA & Mobile

SPA dan mobile tidak memakai session cookie browser dengan mulus — mereka memakai token. Pola paling sederhana dan aman: token per-user yang di-hash di database.

Migration token
bin/rails g migration AddAuthTokenToUsers auth_token:string
RubyModel User dengan token
class User < ApplicationRecord
  has_secure_password
 
  before_create :generate_auth_token
 
  private
 
  def generate_auth_token
    self.auth_token = SecureRandom.hex(32)
  end
end

SecureRandom.hex(32) menghasilkan token acak 64 karakter. Client mengirim token di header Authorization: Bearer <token>:

RubyAutentikasi via token
class ApplicationController < ActionController::API
  before_action :authenticate_user
 
  private
 
  def authenticate_user
    token = request.headers["Authorization"]&.split(" ")&.last
    @current_user = User.find_by(auth_token: token)
    render json: { error: "Unauthorized" }, status: :unauthorized unless @current_user
  end
end

Penyimpanan token di database (bukan JWT stateless) memberi kemampuan revoke instan: saat user logout atau token dicuri, cukup hapus/null-kan auth_token. JWT tetap opsi untuk kasus stateless (SSO antar service) dengan tradeoff: token tidak bisa dicabut sebelum kedaluwarsa.

OAuth & JWT

Untuk integrasi pihak ketiga atau SSO, ekosistem menyediakan:

  • Doorkeeper — OAuth2 provider Rails (client credentials, authorization code).
  • JWT (jwt gem) — token self-contained untuk mikroservice.

Untuk API internal SPA kalian sendiri, token database sederhana sering sudah cukup — hindari kompleksitas sebelum dibutuhkan.

JSON API yang Konsisten

Beberapa keputusan yang membuat API kalian profesional:

  • Response envelope — bungkus data: { "data": [...] } agar mudah menambah metadata (pagination) tanpa breaking.
  • Error format konsisten{ "error": { "code": "...", "message": "..." } } untuk semua status error.
  • Paginationpage/per_page params + header Link atau metadata { "total": 100, "page": 1 }.
  • HTTP status yang tepat — 201 untuk created, 204 untuk deleted, 422 untuk validasi, 401/403 untuk auth.
  • Kebab-case atau snake_case JSON? — Pilih salah satu dan dokumentasikan; Rails default snake_case cocok untuk konsumen internal.
RubyPagination + envelope
def index
  posts = Post.published.includes(:user)
              .page(params[:page]).per(params[:per_page] || 20)
  render json: {
    data: posts.map { |p| PostSerializer.new(p).call },
    meta: { page: posts.current_page, total: posts.total_count }
  }
end

Common Pitfalls

  • Menampilkan password_digest di JSON — gunakan jbuilder/serializer yang memilih field; jangan render json: user.
  • Versi API tidak ada — sekali dipakai eksternal, perubahan jadi breaking; version sejak awal.
  • Token disimpan mentah di database — hash token (misal Digest::SHA256) agar kebocoran DB tidak memberi token valid.
  • Tanpa include untuk relasi — N+1 pada serialization; gunakan includes dan serializer yang memakai data yang sudah dimuat.
  • HTTP status salah — 200 untuk error membuat client sulit memproses; gunakan status semantik.
  • Controller API dengan view logicrender json: murni, tidak mencampur template.

Penutup

Episode 15 membekali kalian Rails API: rails new --api untuk aplikasi ringan tanpa view, jbuilder untuk serialization terkendali, namespace api/v1 untuk versioning, autentikasi token per-user yang bisa di-revoke, dan kebiasaan envelope/error/pagination yang membuat API profesional.

Inti yang harus dibawa pulang:

  • --api menghilangkan view/asset; controller mewarisi ActionController::API.
  • jbuilder memberi kontrol penuh atas bentuk JSON — pilih field yang diekspos.
  • Versioning: namespace :api { namespace :v1 }; versi lama tetap hidup.
  • Token per-user (SecureRandom.hex) bisa di-revoke; JWT untuk kasus stateless.
  • Konsistensi: envelope, error format, pagination, dan HTTP status yang tepat.

Di episode 16 selanjutnya kita akan membedah configuration & environments — Rails Credentials terenkripsi, env vars, konfigurasi per-environment, dan praktik secrets management multi-env. Sampai jumpa di episode 16!

Belajar Ruby on Rails - API Only & Rails API | Belajar Ruby on Rails