A Top Shelf Web Stack—Rails 5 API + ActiveAdmin + Create React App
Blending a rock-solid API and CMS with the absolute best in front-end tooling, built as a single project and hosted seamlessly on Heroku.
Rails is an incredible framework, but sometimes you don’t need all the bulk of the asset pipeline and a layout system. In Rails 5 you can now create an API-only Rails app, meaning you can build your front-end however you like. It’s no longer 100% omakase.
create-react-app, for example.
And for projects that don’t need CMS-like capabilities, that works pretty great straight away.
create-react-app even supports proxying API requests in development, so you can be running two servers locally without having to do any
if NODE_ENV === ‘development’ voodoo. Gosh,
create-react-app is great.
Still, I’ve worked with ActiveAdmin on a few projects, and as an interface between you and the database, it’s pretty unmatched for ease of use. There are a host of customisation options, and it’s super easy for clients to use if you need a CMS.
The issue is that removing the non-API bits of Rails breaks it. Not ideal. But never fear, all is not lost! With a couple of steps you can be running a Rails 5, API-only, serving your
create-react-app client on the front end, with full access to ActiveAdmin.
We’re going to build it, then we’re going to deploy it to Heroku, and then we’re going to celebrate with a drink. Because we will have earned it. Given that theme, we’re going to build an app that shows us recipes for delicious drinks. It’s thematically appropriate!
So, what are we going to use?
- Create React App
All the power of a highly-tuned Webpack config without the hassle.
- Rails in API-only mode
Just the best bits, leaving React to handle the UI.
An instant CMS backend.
- Seamless deployment on Heroku
Same-origin (so no CORS complications) with build steps to manage both Node and Ruby.
And it’ll look something like this:
If you want to skip ahead to the finished repo, you can do so here:
And if you want to see it in action, you do that here:
Let’s get started, shall we?
Step 1: Getting Rails 5 set up
With that delicious low-carb API-only mode
There are a ton of great tutorials on getting Ruby and Rails set up in your local development environment. https://gorails.com/setup/ will work out your operating system, and will walk you through getting Rails 5.0.2 installed.
If you’ve already got Rails 5, awesome. The best way to check that is to run
rails -v in your terminal. If you see
Rails 5.0.1, we’re ready to roll.
Note: At the time of writing the newest version of Rails, 5.1, doesn’t quite work with these steps. Best to stick with 5.0.1 for now.
So, first up, start a new Rails app with the
rails new . --api
Right. We are already part of the way to making a delicious cocktail. Maybe use this time to congratulate yourself, because you’re doing great.
Once the install process has finished, you can fire up Rails:
bin/rails s -p 3001
It’ll do some stuff, eventually telling you that it’s listening on
http://localhost:3001. If you visit it, you should see something like this:
There’s even a kitten! So great.
Step 2: Getting ActiveAdmin working
With a couple of small tweaks to Rails
So, incredibly (and awesomely, which is not a word), you can still get ActiveAdmin working like a charm with this set up. Before you can install it, you just need to switch a couple of Rails classes and add some middleware that ActiveAdmin relies on.
First, you’ll need to swap your
application_controller.rb from using the
API to using
As Carlos Ramirez mentions, this requirement is an unfortunate design decision from ActiveAdmin, as now any controllers we make that inherit from
ApplicationController won’t take advantage of the slimmed down API version.
There is a work around, though. Add a new
api_controller.rb to your
So you can get your controllers to inherit from
ApplicationController. For example:
From there we’ll need to ensure that the middleware has the stuff it needs for ActiveAdmin to function correctly. API mode strips out cookies and the flash, but we can 100% put them back. In your
config/application.rb add these to the
config/application.rb should look something like this:
Do not confuse the flash with The Flash, who is likely heavily trademarked. I don’t have the kind of money I’d need laying around if he turns out to be litigious.
You should also move
gem 'sqlite3' into the
:development, :test group and add
gem 'pg' into the
:production group. Heroku doesn’t support sqlite, and you’ll need to swap those things around once you get to deploying your app. Why not do it now?
Now, keen developers will be sharpening their pitchforks right now, because you should 100% run Postgres locally if you’re developing a Real Application, to ensure your local environment matches your production one. But for the purposes of this exercise, let’s just be roguish and tell no one. They’ll never know.
Bundle and install everything, and then install ActiveAdmin:
bin/rails g active_admin:install
You should see something like the following:
Finally, migrate and seed the database:
Once again you can fire up Rails:
bin/rails s -p 3001
But this time hit
http://localhost:3001/admin. You should see something like this:
And you should take a moment to feel pretty great, because that was a lot.
You can log into ActiveAdmin with the username
email@example.com and the password
password. Security! You can change it really easily in the rad ActiveAdmin environment, though, so fear not.
Step 3: Adding
create-react-app as the client
Yay! Super-speedy Webpack asset handing!
So. We need a front end. If you don’t have
create-react-app yet, install it globally with:
npm install create-react-app -g
And then, in the root of your app, generate it into the
npm install -g create-react-app
It’ll take a bit. You probably have time for a cup of tea, if you’re feeling thirsty.
Once it’s installed, jump in and fire it up:
Right! You have a simple
create-react-app running. That is good. But we can do more than that.
As I mentioned earlier, one of the best bits about working with
create-react-app and an API is that you can automatically proxy the API calls via the right port, without needing to swap anything between development and production. To do this, jump into your
client/package.json and add a proxy property, like so:
client/package.json file will look like this:
(You might wonder why we’re proxying port
3001. Once we hook everything up our scripts will be running the API on port
3001, which is why we’ve been running Rails that way. Nice one picking up on that, though, eagle-eyes. Asking the right questions!)
fetch (along with a bunch of fancy new language features and polyfills you should 100% check out) is included with
create-react-app, so our front end is ready to make calls to the API. But right now that would be pretty pointless—we’ll need some data to actually fetch.
So, let’s get this cocktail party started. We’ll need two relations, the
Drinks, and the
Ingredients that those drinks are made with. You’ll also potentially need a blender, but honestly, a margarita with a couple of ice cubes is still so delicious. Promise.
Now, normally I’d say avoid scaffolding in Rails, because you end up with a ton of boilerplate code that you have to delete, but for the purposes of the exercise, let’s use it. And then delete it. Do what I say, not what I do and all that.
Before that though, I should mention something. One downside to ActiveAdmin using
inherited_resources, which reduces the boilerplate for Rails controllers, is that Rails then uses it when you scaffold anything in your app. That breaks stuff:
Just remind Rails which
scaffold_controller to use in your
config/application.rb and we can be on our way:
config.app_generators.scaffold_controller = :scaffold_controller
application.rb should look something like this, and everything should be right with the world again:
So, scaffolding. First, the
bin/rails g scaffold Drink title:string description:string steps:string source:string
bin/rails g scaffold Ingredient drink:references description:string
Notice that the
Ingredient references the
Drink. This tells the
Ingredient model to
Drink, which is part of the whole
has_many relative database association thing.
See, my Relational Databases 101 comp-sci class was totally worth it.
Unfortunately this won’t tell your
Drink model to
has_many of the
Ingredient model, so you’ll also need to add that to
app/models/drink.rb all by yourself:
Then we can migrate and tell ActiveAdmin about our new friends:
bin/rails generate active_admin:resource Drink
bin/rails generate active_admin:resource Ingredient
Now, Rails is a security conscious beast, so you’ll need to add some stuff to the two files ActiveAdmin will have generated,
app/admin/ingredient.rb. Specifically, you’ll need to permit ActiveAdmin to change your model, which when you think about it is pretty important.
permit_params, you can never edit your delicious drink recipes. Not on my watch.
In our routes, we’ll need to hook up the drinks resource. I like to scope my API calls to
/api, so let’s do that:
Start the server:
bin/rails s -p 3001
And you should be able to visit
http://localhost:3001/api/drinks to see… *drumroll*
Nothing. We should probably add some drinks. To save some time, here’s a
db/seeds.rb that I prepared earlier, featuring a delicious negroni and a delicious margarita:
You’ve already migrated, so it’s just a case of seeding the database:
Now when you refresh you should see:
So, we’re pretty much good to go on the database front. Let’s just massage our scaffolded controllers a little. First, let’s cut back the
DrinksController. We can make sure
def index only returns the
title of each drink, and we can make sure
def show includes the
description of each ingredient of the drink. Given how little data is being sent back, you could just grab everything from
index, but for the purposes of showing how this could work in the Real World, let’s do it this way.
You’ll want to make sure your controllers are inheriting from
And let’s just get rid of 99% of
ingredients_controller.rb, because it’s not going to be doing a lot:
And now we have some fancy data to feed the client. Good for us! This is a big chunk of the setup, and you’re doing great. Let’s celebrate with some Adventure Time, and take this opportunity to think about how great that cocktail is going to be. Talk about earning it:
Next up, let’s create a
Procfile in the root of the app for running the whole Rails /
create-react-app setup locally, because Heroku uses
procfiles to manage your bundle. If you haven’t used them before, you can read about them here.
We’ll call it
Procfile.dev, because while we do need to run a Node server locally we’ll be deploying a pre-built bundle to Heroku, and we won’t need to run a Node server as well. Having a Node server and Rails server locally massively speeds up development time, and it is pretty great, but it’s overkill for production. Your
Procfile.dev should look like this:
Procfiles are managed by
foreman, which if you don’t have installed you can add with:
gem install foreman
And you can fire up the new set up with:
foreman start -f Procfile.dev
Who wants to type that every single time though? Why not make some rake tasks to manage running development and production locally for you? Just add
start.rake to your
You’ll also need to add Foreman to your Gemfile (I’d recommend putting it in your
Then install it:
And from there all you need to do to fire up your development environment is run the super simple:
Glorious! Ten keystrokes to fire up two servers? What magic is this?!
And to test production (which will take a while to build without a grleat deal of output):
Well, that step was a lot. So what’s happening here?
foreman will start the front end,
/client, on port
3000 and the API on port
3001. It’ll then open the client,
http://localhost:3000 in your browser. You can access ActiveAdmin via the API, at
http://localhost:3001/admin, just like you’ve been doing all along.
Now we can sort our the React app. The simplest thing is to just check it works:
In your console, you should see the API call logged:
Which we can 100% use to grab the actual details of each fine beverage from
drinks#show in Rails. Sure, we could’ve just sent everything from the server because it’s only two drinks, but I figure this is closer to how you’d really build something, so let’s go with it.
Now, rather than go through the full front end application, you can either grab the
client folder from the repo:
Or you can install the following dependencies:
npm install semantic-ui-react --save
npm install semantic-ui-css --save
And add them to your
/client app. First, add the css to
And the all the fancy bells and whistles to your
Either way you choose to do it, you’re golden!
You should have a fancy front end that uses Semantic UI and looks something like this:
Step 4: Get everything ready for production
With Rails serving the Webpack bundle
So, how do we get our Rails app serving the Webpack bundle in production?
That’s where the magic of NPM’s
postinstall comes in. We can get Heroku to build the app on the server, and copy the files into the
/public directory to be served by Rails. We end up running a single Rails server managing our front end and back. It’s win win! There are a couple of steps to make that happen.
First up, let’s make a
package.json file in the root of the app, which tells Heroku to compile the
postinstall command will get run after the node build is (you guessed it) installed. First up it’ll build it, then it’ll move the files into
/public. How easy is that?
As always, you are doing great. Also, honestly, my hands are getting sore. On the plus side, this step is super short! Go team!
Step 5: Deploy it to Heroku
And celebrate, because you’ve earned it
We are super close! Soon, everything the light touches will be yours, including a fresh, tasty beverage. So let’s create a new Heroku app and get this thing over the finish line:
Now, if you push to Heroku, this looks like a dual Rails / Node app. But your Node code needs to be executed first so it can be served by Rails. This is where Heroku’s buildpacks come in — they transform your deployed code to run on Heroku. We can tell Heroku, via the terminal, to use two buildpacks (or build processes) in a specific order. First
nodejs, to manage the front end build, and then
ruby, to run Rails:
Let’s make a
Procfile, in the root, for production, which will tell Heroku to run the Rails app:
Before we deploy it’s worth noting that
react-scripts, which manages the build of the client (along with a bunch of other stuff), as a
devDependency in your
package.json. Heroku sets an
NPM_CONFIG_PRODUCTION, to true, which means your build will disregard any
devDependencies and it will fail. Not ideal.
But not unfixable! To overcome this you have two options. You can either set
NPM_CONFIG_PRODUCTION to false:
heroku config:set NPM_CONFIG_PRODUCTION=false
Or you’ll need to move the
react-scripts in your
/client/package.json out of
Both options will work, but given that you’ll likely have more than one
devDependency, it’s easier to tell Heroku to recognise them. Normally I’d say you should always tune your production environment for, well, production, but in this case we’re building the assets with Node, not serving them.
With that sorted, we can deploy this beautiful beverage-based beast:
git add .
git commit -vam "Initial commit"
git push heroku master
Heroku will, following the order of the buildpacks, build the
client, and then fire up Rails.
You’ll need to migrate and seed your database on Heroku, or ActiveAdmin will not be thrilled (and you won’t be able to log in). That’s easy enough though:
heroku run rake db:migrate
heroku run rake db:seed
And there you have it:
When you visit your app you’ll see the
create-react-app on the client side, displaying some delicious drink recipes. You’ll also be able hit
/admin (for example, https://list-of-ingredients.herokuapp.com/admin) and access your database using that truly terrible username and password ActiveAdmin chose for you. I’d recommend changing those on production ASAP. I did, so you can’t just change all my demo recipes to be really rude. I’m gutted you’d even try to, honestly, after everything we’ve been through.
This isn’t exactly the most thrilling demo (especially given it suggests putting prosecco in a negroni, which is a capital crime in some parts of the world) but hopefully it gets you up and running. All the ingredients to make a delicious Rails API / Activeadmin /
create-react-app beverage are here, and the sky’s the limit.
You can see a ready-to-go repo here, too:
Thanks for taking the time to have a look, and I genuinely hope you celebrated with a drink, alcoholic or otherwise. 🍹
Shout out to Roman Rott and Full Stack React for the inspiration to put this together. And a massive thank you to Glen and Xander for taking the time to make suggestions and proofread this small essay for me. 💖
If you have any questions leave a comment or say hi via Twitter. Alternatively, enjoy a GIF of a dog wearing a hat.