How to use Rails ActionCable from an external client application

How to use Rails ActionCable from an external client application

Photo by Umberto on Unsplash

WebSockets is a beautiful way to allow the client-server communication using a persistent bidirectional channel without the client having to poll the server.

In this article I’d like to explain how to get started with websocket in Ruby on Rails from a generic application. I wont’ dive into what is a Websocket or how to create a Rails project. I’ll just leave some links at the end of this article.

First of all, WebSocket in Rails are called ActionCable.

The problem

Tutorials from the official documentation or others like this one explain how to configure the server part and show the code to subscribe and communicate from a Javascript frontend using the rails/actioncable node module. But what if you have an API-only Rails project or you have a frontend other than JS, like an Android or an iOS app? This is why I wrote this article.

Get started!

Let’s suppose we’ve a Rails project and you run the command below

rails generate channel Chat

it will generate a file called <your_name>_channel.rb where we can handle the client subscriptions or/and we can write custom methods that will be called by the clients… To keep the things simple we’ll implement the subscribed method and we write from scratch a custom dummy method (my_method) that receive a data parameter. We’ll see later what data is. Let’s replace <your_name> with ChatChannel for this example. Here’s how file looks like.

class ChatChannel < ApplicationCable::Channel
  def subscribed
    logger.info "subscribed"
    stream_from "chat_channel"
  end

  def my_method(data)
    puts data
  end
end

Our goal is reach the subscribed and the my_method methods from an external client other than JS or out of our Rails project. Let’s start!!!

Photo by Braden Collum on Unsplash

First question: how to create a WebSocket client? Or can we use a “Postman” like client already available?

There are many ways and libraries to establish a WebSocket connection for many platforms, but to get started I suggest this ready to use WebSocket clients

Second question: which URL should I call? In my project routes I can’t see anything related to ActionCable

  • Firstly we have to change the protocol from http/https to ws.
  • Let’s suppose that our Rails project runs on localhost, port 3000.
  • By default without specifying anything else, Rails expose the WebSocket service on /cable path

Putting these things together the URL is ws://localhost:3000/cable

Third question: how we communicate?

Basically with JSON messages as specified by WebSocket protocol. Like the rails/actioncable JS module, libraries provides an “SDK way” to manage these messages, but in our simple web clients we have a “Message field” to write the command in, nothing more. I strived to find what to format the request and at the time I write I guess there is a lot more to find out. To guide me to the right direction i found these two articles (1, 2).The 2 also explain how to authenticate the user. To see the things working for a “step 0” in a simple way I’ll ignore this step. From the 1 I tried to copy/paste the string in the sendTXT method in the web client Message field as is (even with begin/end double quotes and all escape characters: “{\\”command\\”:\\”subscribe\\”,\\”identifier\\”:\\”{\\\\\\”channel\\\\\\”:\\\\\\”ArduinoChannel\\\\\\”}\\”}” ) but it didn’t work (I changed the name to my channel name first). I tried to remove the begin/end quotes and I thought that a problem was that the escape characters were too many. Hence I tried to serialize a dictionary representing the JSON above with the help of the Rails console . I finally obtained this JSON:

{"command": "subscribe","identifier": "{\"channel\": \"ChatChannel\"}"}

It works!!!! The web console answerd me “welcome”! Just take a look at the Rails log to be sure that the subscribed method had been called and printed “subscribed”. Yes!!! All right, then: How to call my_method method now?

Here’s the best part: we can pass as many parameters we want and we can call any methods with a very similar JSON

{"command": "message","identifier": "{\"channel\": \"ChatChannel\"}", "data":"{\"action\": \"my_method\", \"code\":\"a\"}"}

The differences:

  • the command type has changed from “subscribe” to “message”
  • identifier must remain the same as the subscription
  • we added the data key with a JSON string (with all the escape characters)

Let’s send this message from the console and have a look at the Rails log. We’ll see these lines

  • ChatChannel#my_method({“code”=>”a”}). It recognized the action keyword and interpreted the JSON as an hash parameter. If we print the data parameter we obtain indeed {“action”=>”my\_method”, “code”=>”a”}

Our goal has reached!!! Great!

Just a little last thing: in a normal Rails (http) controller method you can write this code:

ActionCable.server.broadcast("chat_channel","An update has arrived...")

You’re dispatching via WebSocket on the chat_channel the “An update has arrived…” string: this will be printed even on your web console. Doesn’t it look like a notification

Further references

To learn about WebSocket or creating a Rails project

Thanks for reading…