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!