Building A Chat Feature With ActionCable

I recently created a board game app using ActionCable in my Ruby on Rails API and ActionCable Provider for React in my front end to establish WebSocket connections between each client and my API.

I ran into a lot of WebSocket trouble when trying to deploy this app to Heroku. I found a lot of different suggestions for how to fix the issue and just about tried them all.

I do not wish that brand of despair on anyone, so I distilled all of the fixes down to what actually solved the problem for me and wrote the following step-by-step guide for deploying an app utilizing ActionCable to Heroku.

This post assumes you have already successfully implemented ActionCable in your development environment. If you have not, check out this excellent primer on how to setup ActionCable in a Rails/React app by Dakota Lillie (this was my guiding light through the ActionCable darkness).

I’m also assuming you’ve already deployed your Rails API to Heroku and came across this blog as you desperately scoured the Internet for answers to your errors. If not, follow Heroku’s documentation to initially deploy your API.

To begin, take a look at the config/cable.yml file in your Rails directory. You’ll see that under development the adapter is set to async, but under production it’s set to redis. Also, production has two additional properties: url and channel_prefix.

Unless you’re planning to have more than one app access your API, you can comment out the channel_prefix bit. As for the url part, we’ll get to that in a moment.

One thing, at least, is clear; in order to use ActionCable in production, we’ll need to setup Redis. Go to your Gemfile and uncomment gem ‘redis’, ‘~> 4.0’. Then run bundle in your terminal to install the gem.

Now we need to add the redistogo add-on to your Heroku app. From your Rails directory, type heroku addons:create redistogo:nano.

Heroku will prompt you to verify your billing information if you do not already have a credit card setup on your account. Don’t worry, the Nano plan designated in the above command is free. It’s fairly limited (it only allows 10 connections), but it’ll get your app up and running. Here’s the pricing information for all redistogo plans if you anticipate higher traffic.

After you’ve added on redistogo, you’ll receive a Redis URL. Type heroku config — app <your heroku app name> | grep REDISTOGO_URL in your terminal to view this URL. Your Heroku app name is the pleasant phrase and string of numbers preceding herokuapp.com in your Heroku domain (mine is whispering-crag-66572).

Copy the Redis URL from your terminal and go back to theconfig/cable.yml file in your Rails API. Replace whatever’s after url: in production with your URL. Do not put quotes around your Redis URL.

Now open the config/environments/production.rb file in your Rails API. Scroll down until you find the following:

# Mount Action Cable outside main process or domain.# config.action_cable.mount_path = ‘/cable’# config.action_cable.url = ‘example.com’# config.action_cable.allowed_request_origins = [ ‘http://example.com', /http:\/\/example.com.*/ ]

Uncomment the last two lines and fill in your Heroku URLs like this:

config.action_cable.url = 'wss://whispering-crag-66572.herokuapp.com/cable'config.action_cable.allowed_request_origins = [ 'https://fathomless-cove-56346.herokuapp.com', /http:\/\/fathomless-cove-56346.herokuapp.com.*/ ]

The first lineconfig.action_cable.url is where you put the ActionCable route you setup in your config/routes.rb file. My route looks like this:

mount ActionCable.server => ‘/cable’

So, myconfig.action_cable.url is the domain Heroku provided for my Rails API plus my ActionCable route.

You might have noticed that I’m using wss:// for this instead of thews:// you most likely used in development to preface your WebSocket route in React. Because your Heroku domain uses the secure https://, your Rails API will dually use the secure WebSocket protocol, wss://. Make sure to account for this difference on your front end.

The second line, config.action_cable.allowed_request_origins, looks a little strange, but it’s pretty straightforward. The first item in the array is the https:// Heroku domain for my React app written as a string.

Make sure that this URL matches exactly what your front end is sending as the HTTP_ORIGIN. I wasted a couple hours debugging only to find that I just needed to remove a slash at the end of my URL to get everything working.

If this is giving you trouble, you can find the HTTP_ORIGIN in the Heroku logs for your API. While in your Rails directory, type heroku logs --tail into the terminal to track your logs. If the WebSocket connection is failing, you should see blocks of logs that look like this:

I feel like a hacker in a movie when I look at these logs

On the right-hand side of the picture above, you should see a log (four lines from the top) that says Request origin not allowed: https://fathomless-cove-56346.herokuapp.com. That URL is the HTTP_ORIGIN that my front end sends in the header when establishing the WebSocket connection.

Ok, so that covers the first item in the allowed_origins array. I went down a little bit of a tangent, so let’s take another look at what I’m talking about:

config.action_cable.allowed_request_origins = [ 'https://fathomless-cove-56346.herokuapp.com', /http:\/\/fathomless-cove-56346.herokuapp.com.*/ ]

The second item in the above array is the regex version of your front end’s Heroku domain using http://. I believe this is written in regex to cover any http requests that are not secure. For example, I discovered that this allows for requests from both fathomless-cove-56346.herokuapp.com and http://fathomless-cove-56346.herokuapp.com.

Now, push your Rails API to Heroku again, and that should do it! I hope this guide can save at least one other person from the pit of debugging despair I found myself in.

Github links for my API and Front End if you want to take a look at other parts of the code.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store