Ruby on Rails has long been celebrated for its ‘convention over configuration’ philosophy, simplifying web development for countless programmers. However, what if you’ve started with a lean Rails API-only application and now find yourself needing a front-end? This isn’t uncommon, especially with the rise of JavaScript frameworks and SPAs. Dive in with us as we explore how to seamlessly add a vanilla Rails front-end to your API-only application, merging the best of both worlds for a robust web application experience.

Rails API-only applications

Rails is known for its efficiency and convention over configuration philosophy, which offers developers the flexibility to create different types of web applications. An API-only Rails application is designed to serve as a back-end, handling data and logic while interacting with front-end clients through HTTP requests.

The decision to opt for the API-only mode for a Rails application is often made when generating a new project. This is typically when planning to integrate with a front-end framework like React, when the application is intended to serve as a back-end for mobile applications, or when it's one of several microservices. By stripping away the view layer and asset pipeline, Rails operates more lightweight and contains less bloat.

In contrast to a full-stack Rails application, the API-only mode omits middleware for handling cookies and sessions, and excludes the ActionView module. Beyond that, the asset pipeline is not included, as it's primarily used for managing front-end assets, such as stylesheets, JavaScript files, and images. This streamlined version of Rails is optimized for creating APIs but leaves the handling of user interfaces to other tools or frameworks.

Requirements change, and there might come a time when adding a front-end directly to your Rails application becomes necessary. Perhaps you want to create an admin panel or simply provide a web-based interface for a portion of your application’s functionality. Transitioning from an API-only application to a full-stack Rails application is possible!

Preparing a Rails API for a front-end

Transitioning from an API-only Rails application to a full-stack version involves reintegrating certain components that are vital for handling views and assets. This section will guide you through the process of adding back the necessary middleware and making the required configurations to bring a front-end to your Rails API application.

If you're starting from scratch, you can install Rails 7.1.1 with the following:

gem install rails 7.1.1

Next, you can generate a new API-only Rails application with the following:

rails _7.1.1_ new my_api_name --api

In Rails, middleware serves as a way to process requests and responses entering and leaving the application. In a full-stack Rails application, various types of middleware are employed to manage sessions, cookies, and assets. However, in an API-only setup, these are stripped away. To prepare your application for a front-end, you’ll need to reintroduce the middleware. You can do this by adding the following lines to your config/application.rb file:

config.middleware.use ActionDispatch::Flash
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore

These typically aren't needed in a Rails application because they are included by default, so we are adding them back here. Next, at the top of the same file, we'll require some modules for handling views and assets. Add the following near the existing require lines:

require "sprockets/railtie"
require "action_view/railtie"

Your new config/application.rb should now look like this:

require_relative "boot"

require "rails/all"
require "sprockets/railtie"
require "action_view/railtie"


# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module SampleApi
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 7.1

    # Please, add to the `ignore` list any other `lib` subdirectories that do
    # not contain `.rb` files, or that should not be reloaded or eager loaded.
    # Common ones are `templates`, `generators`, or `middleware`, for example.
    config.autoload_lib(ignore: %w(assets tasks))

    # Configuration for the application, engines, and railties goes here.
    #
    # These settings can be overridden in specific environments using the files
    # in config/environments, which are processed later.
    #
    # config.time_zone = "Central Time (US & Canada)"
    # config.eager_load_paths << Rails.root.join("extras")

    # Only loads a smaller set of middleware suitable for API only apps.
    # Middleware like session, flash, cookies can be added back manually.
    # Skip views, helpers and assets when generating a new resource.
    config.api_only = true
    # Add back middleware for cookies and sessions
    config.middleware.use ActionDispatch::Flash
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use ActionDispatch::Session::CookieStore
  end
end

You'll also need to add sprockets to your Gemfile. Add the following line:

gem 'sprockets-rails', :require => 'sprockets/railtie'

Then, run:

bundle install

Next, you'll need to add a manifest file to manage asset precompilation. In the app directory of your project, add a new folder called assets. Inside this directory, add a new folder called config. Add a folder inside the assets directory called builds, another called images, and another called stylesheets. The assets directory should now contain builds, images, stylesheets, and config.

Inside this new app/assets/config directory, add a new file titled manifest.js. The manifest file should contain the following:

//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js
//= link_tree ../builds

In the app/assets/builds and the app/assets/images directories, create a file called .keep that is empty. In the app directory, create a directory called javascript. In the vendor directory, create a directory called javascript.

With these changes, your Rails application is now ready to accommodate a front-end, serving assets and views as a full-stack Rails application would. You can learn more about sprockets and the asset pipeline in the official Rails documentation. In the following sections, we will dive into creating your first view and integrating front-end assets to truly bring your user interface to life.

Building the first view

With your Rails API now ready for full-stack development, it's time to construct the first view. This visual component is where users will interact with your application. We'll begin by setting up a controller that connects to a view and establish a route to make the view accessible via a web browser.

For a front-end view, you'll create a controller dedicated to gathering the necessary data and passing it on to the view. You can use the Rails generators to help with this:

$ rails generate controller Courses index

This creates a CoursesController with an index action. It also creates a series of related files, including views, helper files for these views, a functional test file, and a JavaScript file for the controller.

After running the generator command, you'll need to set up a root route that points to the course controller's index action. This tells Rails where to direct requests to the root URL of your application. Update your config/routes.rb file like so:

Rails.application.routes.draw do
  root 'courses#index'
  # rest of file
end

Next, change ApplicationController to inherit from the full ActionController rather than just the API module. The class definition should now look like this:

class ApplicationController < ActionController::Base

Next, add the associated view file and directory for the courses index. In the app/views directory, create a new directory titled course. Inside the app/views/courses directory, add a new index.html.erb file with the following contents:

<h1>Welcome to Our Application!</h1>
<p>Find me in app/views/courses/index.html.erb</p>

The application won't render the page without application.html.erb. In the app/views/layouts directory, add a new file called application.html.erb. It should look like this:

<!DOCTYPE html>
<html>
<head>
  <title>SampleApi</title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>
  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
</head>
<body>
  <%= yield %>
</body>
</html>

We’ve made considerable progress, but the application is not quite ready to serve an HTML page without front-end assets, which we'll explore next.

Integrating front-end assets

First, add the saas gem to the Gemfile by adding the following line:

gem 'sass-rails'

Then, add the following:

bundle install

Next, in app/assets/stylesheets, add a new file called application.css. Now add in the default comment and linking:

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require_tree .
 *= require_self
 */

Conclusion

While expanding an existing application to include a front-end is quite an involved task, it's often better than starting from scratch. In this article, we've gone over how to add missing middleware, install missing dependencies, create missing scaffolding, and finally, render a view from our Rails app!

When working with a Rails 7 application, it's important to decide whether you'll use import maps or something like Webpacker. Import maps are the default in Rails 7, but you'll need to add them if you're converting an API-only application.

The Rails Import Maps Documentation goes into great detail on the process, as well as some alternate options.

Now that your API application is a fully-featured, full-stack Rails application, you can proceed with a vanilla Rails front-end or expand it to include React or Vue.

Have fun building user interfaces with Rails!

Get the Honeybadger newsletter

Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo
    Jeffery Morhous

    Jeff is a Software Engineer working in healtcare technology using Ruby on Rails, React, and plenty more tools. He loves making things that make life more interesting and learning as much he can on the way. In his spare time, he loves to play guitar, hike, and tinker with cars.

    More articles by Jeffery Morhous
    An advertisement for Honeybadger that reads 'Turn your logs into events.'

    "Splunk-like querying without having to sell my kidneys? nice"

    That’s a direct quote from someone who just saw Honeybadger Insights. It’s a bit like Papertrail or DataDog—but with just the good parts and a reasonable price tag.

    Best of all, Insights logging is available on our free tier as part of a comprehensive monitoring suite including error tracking, uptime monitoring, status pages, and more.

    Start logging for FREE
    Simple 5-minute setup — No credit card required