A Top Shelf Web Stack—Rails 5 API + ActiveAdmin + Create React App

Charlie Gleason
superhighfives
Published in
14 min readMar 6, 2017

--

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.

Update, 16 May 2018: First off, thank you to everyone who has taken the time to give feedback, share their experiments, and clap this post. Since I finished this last year I joined Heroku, and with their help I’ve rewritten it to iron out the bugs, implement your feedback, and to include support for Rails 5.2.0, React Router, and Yarn.

You can find version 2.0 of this post here: https://blog.heroku.com/a-rock-solid-modern-web-stack

You can also find latest repo, including a Heroku button you can automagically deploy with, here: https://github.com/heroku/list-of-ingredients

Once again, I could not be more appreciative of your support and feedback.

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.

Like using 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.
  • ActiveAdmin
    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:

Our app, List of Ingredients, which really does what it says on the tin.

If you want to skip ahead to the finished repo, you can do so here:
https://github.com/superhighfives/list-of-ingredients

And if you want to see it in action, you do that here:
https://list-of-ingredients.herokuapp.com/

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.1 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 --api flag:

mkdir list-of-ingredients
cd list-of-ingredients
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:

Yay Rails!

There’s even a kitten! So great.

Step 2: Getting ActiveAdmin working

With a couple of small tweaks to Rails

(Thanks to Roman Rott for this bit.)

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 Base:

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 app/controllers:

So you can get your controllers to inherit from ApiController, not ApplicationController. For example:

Huzzah!

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 Application class:

Your 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.

I’m sure you do, moneybags.

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:

bundle install
bin/rails g active_admin:install

You should see something like the following:

Finally, migrate and seed the database:

bin/rake db:migrate
bin/rake db:seed

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:

(Yours will be called List of Ingredients, but I was still working out what to build at this point. Shame on me for not taking a fresh screen grab. This is amateur hour.)

And you should take a moment to feel pretty great, because that was a lot.

You can log into ActiveAdmin with the username admin@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!

(Shout out to Full Stack React for this bit.)

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 /client folder:

npm install -g create-react-app
create-react-app client

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:

cd client
npm start

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:

"proxy": "http://localhost:3001"

Your 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:

"Could not find" is never a good start to the last line of output.

Fortunately, this is a solvable problem. You just need to tell Rails to use the regular scaffolding process. You know, from when we were young and scrappy and people didn’t say JavaScript fatigue like having options is a bad thing. The Good Old Days.

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

Your application.rb should look something like this, and everything should be right with the world again:

Crisis averted!

An artist’s representation of me after spending hours trying to understand this particular error by typing every variation of it into StackOverflow.

So, scaffolding. First, the Drink model:

bin/rails g scaffold Drink title:string description:string steps:string source:string

Then, the Ingredient model:

bin/rails g scaffold Ingredient drink:references description:string

Notice that the Ingredient references the Drink. This tells the Ingredient model to belong_to the 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/rake db:migrate
bin/rails generate active_admin:resource Drink
bin/rails generate active_admin:resource Ingredient

Go team.

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/drink.rb and app/admin/ingredient.rb. Specifically, you’ll need to permit ActiveAdmin to change your model, which when you think about it is pretty important.

First up, app/admin/drink.rb:

Then app/admin/ingredient.rb:

Without 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:

Now, not everyone is super keen on alcoholic beverages, so feel free to swap the booze out for something a little more low key, like sparkling apple juice, which is hands down the best drink ever invented.

You’ve already migrated, so it’s just a case of seeding the database:

bin/rake db:seed

Now when you refresh you should see:

Yay! Drinks!

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 id and title of each drink, and we can make sure def show includes the id and 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 ApiController, too.

And let’s just get rid of 99% of ingredients_controller.rb, because it’s not going to be doing a lot:

So minimal!

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 /lib/tasks folder:

You’ll also need to add Foreman to your Gemfile (I’d recommend putting it in your :development group:

Then install it:

bundle install

And from there all you need to do to fire up your development environment is run the super simple:

bin/rake start

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 great deal of output):

bin/rake start:production

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:

http://github.com/superhighfives/list-of-ingredients

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 client/src/index.js:

And the all the fancy bells and whistles to your client/src/app.js:

I should clarify that this is what I like to call “proof of concept code”, rather than “fancy commented refactored code”.

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:

So close! So, so close!

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 package.json‘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 create-react-app. 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:

heroku apps:create

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 create-react-app defines 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 ENV var, NPM_CONFIG_PRODUCTION, to true, which means your build will disregard any devDependencies and it will fail. Not ideal.

We were so close!

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 devDependencies into dependencies:

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:

https://list-of-ingredients.herokuapp.com/

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.

It’s worth noting, if you’re planning to use react-router from here, there’s a few more steps you’ll need to take. Logan asked about it, so thanks Logan!

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:
http://github.com/superhighfives/list-of-ingredients

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.

Yesssssssssss.

--

--

Charlie Gleason
superhighfives

A thirty-something with feelings, interested in outer space and the acquisition and distribution of high fives.