Action Cable comes built into Rails. Let's configure it for development and production.
Configuration Files
Action Cable configuration lives in:
yaml# config/cable.yml development: adapter: async test: adapter: test production: adapter: redis url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> channel_prefix: myapp_production
Adapters
- async: In-process, good for development (single server)
- redis: Production-ready, supports multiple servers
- postgresql: Uses Postgres NOTIFY/LISTEN
Mount the Server
ruby# config/routes.rb Rails.application.routes.draw do mount ActionCable.server => '/cable' # Your other routes... end
The Connection Class
Handles WebSocket authentication:
ruby# app/channels/application_cable/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 # Session-based auth if verified_user = User.find_by(id: cookies.encrypted[:user_id]) verified_user else reject_unauthorized_connection end end end end
Alternative: Token Authentication
ruby# app/channels/application_cable/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 token = request.params[:token] if verified_user = User.find_by(auth_token: token) verified_user else reject_unauthorized_connection end end end end
Client connects with:
javascriptcreateConsumer(`/cable?token=${userToken}`)
The Channel Base Class
ruby# app/channels/application_cable/channel.rb module ApplicationCable class Channel < ActionCable::Channel::Base # Shared channel logic end end
Client-Side Setup
Rails generates JavaScript for Action Cable:
javascript// app/javascript/channels/consumer.js import { createConsumer } from "@rails/actioncable" export default createConsumer()
Import Maps Configuration
ruby# config/importmap.rb pin "@rails/actioncable", to: "actioncable.esm.js" pin_all_from "app/javascript/channels", under: "channels"
Testing Connection
Start Rails and check the console:
Started GET "/cable" [WebSocket]
Successfully upgraded to WebSocket
In browser console:
javascriptApp.cable.connection.isOpen() // true