Adding views to an API-only Rails app

What if you started with a Rails API-only application and now find yourself needing a front-end? In this article we explore how to add a vanilla Rails front-end to your API-only application.

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!

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. 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
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial