Membedah Action Cable: WebSocket sebagai fondasi realtime, channel dan streaming, autentikasi koneksi, integrasi dengan Turbo Streams, serta praktik membangun notifikasi realtime

Setelah di episode 13 kalian mengelola file upload, episode 14 masuk ke dunia yang paling "ajaib" dalam Rails: realtime. Dengan Action Cable, Rails bisa mendorong data ke browser secara instan tanpa browser harus menanyakan ulang — untuk chat, notifikasi, atau update status.
Mengapa episode ini penting? Karena persepsi pengguna tentang aplikasi modern adalah sesuatu yang terjadi begitu saja: komentar baru muncul tanpa reload, notifikasi muncul tanpa refresh. Action Cable membangun ini di atas WebSocket — koneksi dua arah yang persisten — dan ketika digabungkan dengan Turbo Streams dari episode 7, realtime Rails menjadi mulus tanpa satu baris JavaScript manual.
HTTP adalah request-response: browser meminta, server menjawab, koneksi ditutup. Untuk tahu apakah ada data baru, browser harus bertanya lagi (polling). WebSocket berbeda:
WebSocket membuka koneksi sekali lalu server bisa mengirim kapan saja. Action Cable mengelola siklus hidup koneksi, autentikasi, dan broadcast.
Channel adalah unit logika realtime — analogi: channel adalah ruangan, dan broadcast adalah pengumuman ke semua yang ada di ruangan. Buat channel dengan generator:
bin/rails g channel Notificationsclass NotificationsChannel < ApplicationCable::Connection
def subscribed
stream_from "notifications_#{current_user.id}"
end
def unsubscribed
stop_all_streams
end
endstream_from "notifications_#{current_user.id}" membuat saluran unik per user. Client (browser) subscribe ke channel yang sama:
import consumer from "./consumer"
consumer.subscriptions.create("NotificationsChannel", {
received(data) {
document.querySelector("#notifications").insertAdjacentHTML(
"afterbegin",
`<p>${data.body}</p>`
)
}
})Koneksi WebSocket harus tahu siapa yang terhubung. Autentikasi dilakukan di connection.rb:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
user_id = session[:user_id] || cookies.signed[:user_id]
User.find_by(id: user_id) || reject_unauthorized_connection
end
end
endidentified_by :current_user membuat current_user tersedia di channel; reject_unauthorized_connection memutus koneksi tanpa auth. Ini garis pertahanan pertama realtime — jangan pernah broadcast ke stream publik tanpa filter identitas.
Server mengirim data kapan pun — dari controller, job, atau model:
def create
@comment = @post.comments.new(comment_params)
if @comment.save
NotificationsChannel.broadcast_to(current_user, { body: "Post #{@post.title} mendapat komentar baru" })
redirect_to @post
else
render :new, status: :unprocessable_entity
end
endbroadcast_to(recipient, payload) mengirim ke stream user tertentu. Untuk yang lebih umum, ActionCable.server.broadcast("stream_name", payload) broadcast ke semua subscriber stream tersebut.
Kombinasi Action Cable + Turbo Streams adalah killer feature realtime Rails: server mengirim HTML Turbo Stream, dan Turbo menerapkannya ke halaman secara otomatis — tanpa JavaScript custom:
def create
@comment = @post.comments.new(comment_params)
if @comment.save
@comment.broadcast_append_to @post
redirect_to @post
else
render :new, status: :unprocessable_entity
end
endbroadcast_append_to @post (disediakan broadcasts di model) mengirim Turbo Stream append yang otomatis menambahkan _comment.html.erb ke halaman semua user yang sedang melihat post tersebut — tanpa mereka harus me-refresh. Realtime di Rails 8 jadi semudah satu baris.
Tip
Untuk chat sederhana, framework mental yang perlu diingat: browser subscribe ke channel, server broadcast ke stream, Turbo menerapkan HTML. Tidak perlu JavaScript framework — koneksi dikelola Action Cable, UI dikelola Turbo Streams.
Di production, WebSocket berjalan sebagai bagian dari server (atau adapter terpisah). Rails 8 dengan Solid Cable bisa memakai database sebagai pub/sub backend tanpa Redis:
config.action_cable.url = "/cable"
config.action_cable.mount_path = "/cable"
# Solid Cable default: database-driven pub/subconfig.action_cable.url = "/cable" memastikan browser mengarah ke mount path yang benar saat app di belakang proxy. Solid Cable menghapus ketergantungan Redis untuk kebanyakan aplikasi — kita bedah lebih dalam di episode 22.
current_user.id di nama stream atau pakai broadcast_to.find_verified_user harus diverifikasi; reject_unauthorized_connection adalah default yang aman.consumer.subscriptions.create cocok dengan nama channel.Upgrade: websocket headers; tanpa itu koneksi hang.Episode 14 membekali kalian Action Cable: WebSocket sebagai koneksi persisten, channel sebagai ruangan data dengan stream_from/broadcast_to, autentikasi di connection.rb, dan integrasi dengan Turbo Streams yang membuat realtime Rails semudah broadcast_append_to.
Inti yang harus dibawa pulang:
subscribed → stream_from, server kirim via broadcast.ApplicationCable::Connection via identified_by.Di episode 15 selanjutnya kita akan membedah API Only & Rails API — rails new --api, serialization (jbuilder), versioning API, dan token auth untuk SPA & mobile. Sampai jumpa di episode 15!