How to Deploy a Rails Application and Add Authentication using Devise
Introduction
Ruby on Rails is a Ruby web application framework that helps make building modern web applications an easy experience. It powers software created by companies like Airbnb, GitHub, Soundcloud, Shopify, and a host of other companies.
User authentication is a common feature present in most software products. It is especially important in software products where the identity of a user is required before they are granted access to the system.
In this tutorial, you will learn how to implement authentication in a Rails application by using Devise, an authentication solution for Rails. We will deploy this application on Koyeb and enjoy the platform's built-in autoscaling, automatic HTTPS, global load balancing, and more. With Koyeb's git-driven deployment, all changes you make to your application will be automatically built and promoted once the build and necessary healthchecks are complete.
Requirements
To successfully follow this tutorial, you need the following:
- Ruby programming language installed on your local development machine. This tutorial uses version 2.6.6 of Ruby.
- Ruby on Rails framework installed on your local development machine. This demo application in this tutorial is built with Rails 6.1
- Node.js 10.17 or higher installed on your local development machine.
- Yarn package manager installed on your local development machine.
- PostgreSQL installed on your local development machine. This tutorial uses version 13 of PostgreSQL.
- An ElephantSQL account to get a PostgreSQL database.
- A Koyeb account to deploy the demo application.
Steps
The steps to implementing authentication in a Rails application and deploying it to Production on Koyeb include:
- Creating a new Rails application.
- Setting up the database.
- Creating the application homepage.
- Adding messages to the application.
- Integrate Authentication to the application with Devise.
- Deploying the Rails application to Koyeb.
Creating A New Rails Application
In this section, you will create a new Rails application. This demo application will contain messages that should be visible to only authenticated users.
Rails provides a set of commands that make building web applications an easy experience. One such command is the new
command, used to generate a new Rails application. To create a new Rails application, open up a terminal window on your machine and run the following command:
rails new secret_message -d=postgresql -T
The command above creates a new Rails application in a directory named secret_message
using the options passed to the new
command and installs all necessary dependencies. The options passed to the new
command specify the following:
- The
-d
flag specifies the type of database to be set up for the application. The demo application for this tutorial will use a Postgres database. - The
T
flag ensures that dependencies required for writing tests are not installed.
If run successfully, the command will return a bunch of output that ends with text similar to the one below:
├─ websocket-extensions@0.1.4
└─ ws@6.2.2
✨ Done in 13.84s.
Webpacker successfully installed 🎉 🍰
Once the command is done running, move to the created secret_message
directory in your terminal by running:
cd secret_message
The secret_message
directory is the root directory of the demo application and is where subsequent commands will be run to build out the demo application. To see your new Rails application in the browser, run the command below in your terminal window:
rails server
The command above fires up a Puma server, which is Rails' default web server, and serves the Rails application on port 3000. To see the application, navigate to http://localhost:3000
in your browser, and you will be greeted with Rails' default welcome page.
To stop the Puma server at any time, press the CTRL+C
button in the terminal window where it is running. This will stop the running server, and the application will no longer be accessible in the browser.
With the Rails application successfully created, it will be connected to a database in the next section.
Setting Up The Database
Your new Rails application needs a database to store the messages to be shown to authenticated users. In this section, you will connect the demo application to a PostgreSQL database.
Rails offers the ability to set up a different database per software life cycle environment. The database config file found in config/database.yml
contains sections to provide database information for the development, test and production environment. The database config file comes pre-filled with database config values for each environment, and these values can be altered as preferred. The default database config for the demo application is shown below:
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: secret_message_development
test:
<<: *default
database: secret_message_test
production:
<<: *default
database: secret_message_production
username: secret_message
password: <%= ENV['SECRET_MESSAGE_DATABASE_PASSWORD'] %>
For the sake of this tutorial, we will use the default database config provided by Rails. To set up a database for a Rails application using the database config values, Rails provides a db:prepare
command. This command creates databases using values set in the database config. While in the secret_message
directory in your terminal, run the following command to create the necessary databases:
rails db:prepare
This command creates a development and test database and returns the following output when run successfully:
Created database 'secret_message_development'
Created database 'secret_message_test'
With that, you have successfully connected your Rails application to a PostgreSQL database. In the next section, you will create a homepage for the application.
Creating the Homepage
The demo application currently displays the Rails' welcome page as its home page, and in this section, you will replace that with a new homepage.
Rails adopts the Model-View-Controller or MVC pattern of software design. A Rails controller sits between a view and a model, receiving requests and responses and passing them to their corresponding view or model. Creating a homepage for the demo application involves creating a controller and view to handle requests to view the homepage.
For the demo application, requests to view the homepage as well as to view existing messages in the application will be handled using the same controller. Rails provides a controller
generator command for creating controllers. To create a controller for the homepage and messages, run the command below in the demo application's root directory in your terminal window:
rails generate controller SecretMessages home index show --skip-assets --no-helper
The controller generator command above accepts three actions; home
, index
and show
. Rails generates a controller action and a corresponding view file for each action passed to it. A controller action in Rails is a method in a controller class that matches a route. It contains the code to be executed when its matching route receives a request. Two additional options are passed to the generator command; the --skip-asset
option prevents the generation of CSS asset files for views, while the --no-helper
option prevents the creation of a controller helper file.
Running the command generates four files in the project directory. They include:
- A
secret_messages_controller.rb
controller with three methods that correspond to the actions passed to the generator command. - A
home.html.erb
file which will be the view for the homepage. - An
index.html.erb
file which will be the view for displaying a list of messages in the application. - A
show.html.erb
file which will be the view for displaying individual messages.
In addition to generating controller and view files, the command updates the routes file located at config/routes.rb
. It adds a get
route for each action passed to the command. The routes added to the routes file by the controller generator command need to be edited to specify the application's root route.
A root route in Rails is a route that specifies the page to be displayed when users visit the root URL of a Rails application. In your preferred text editor, open the routes file located at config/routes.rb
and make the changes below to the three get routes in the file:
get 'secret_messages/home' # [!code --]
get 'secret_messages/index' # [!code --]
get 'secret_messages/show' # [!code --]
root 'secret_messages#home' # [!code ++]
get '/secret_messages', to: 'secret_messages#index' # [!code ++]
get '/secret_message/:id', to: 'secret_messages#show', as: :secret_message # [!code ++]
In the code above, changes are made to the routes generated by the controller generator command. With the root
keyword, the root route is mapped to the home
action of the SecretMessages
controller. With this, Rails routes requests made to the root URL (/
) of the application to the home
action of the SecretMessages
controller, and its corresponding view is displayed. Also, the /secret_messages
route is mapped to the index action of the secret messages controller, while an id
param is added to the last route, and it is mapped to the show action of the secret message controller. The route is also named secret_message
using the as
keyword. Make sure to save the file after making the changes.
With the root route now set up, the application's homepage will no longer display Rails' default welcome page. Before viewing the new homepage, we will add Simple.css to the application. Simple.css is a classless CSS framework that offers basic styles to semantic HTML.
In your text editor, open the application.html.erb
file located at app/views/layouts/application.html.erb
and add the code below to the <head>
section of the HTML:
<head>
...
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css" />
</head>
The link loads minified Simple.css styles into the application. Next, replace the markup in the homepage view located at app/views/secret_messages/home.html.erb
with the following code:
<h1>Secret Messages</h1>
<figure>
<img alt="Image" src="https://i.imgur.com/XZcDi6Q.jpeg" width="83%" height="auto" />
<figcaption>Source: https://imgur.com/gallery/A3fia</figcaption>
</figure>
<p>Secret messages for your eyes only</p>
This adds an image and text for a customized homepage. With that change, save the file and start the Rails server in your terminal window by running rails s
and view the application in your browser at http://localhost:3000
. You will see the image below:
In this section, you successfully set up a homepage for the demo application, and in the next section, you will add messages to the application.
Adding Messages to the Application
After setting up the demo application's homepage, you will create a model to handle messages in the application in this section.
A Rails model is a class that represents an object, in this case, a message, and facilitates the creation and manipulation of said objects between the database and a Rails application. As with most tasks in a Rails application, Rails provides a generate model
command to create a model in a Rails application. The command accepts a model name along with its properties and data types. The message object for the demo application will have two properties: a title and a body.
Run the command below in your terminal window to create a Message
model:
rails generate model message title:string body:text
Running the command above instructs Rails to create a Message
model with a title and body property, both having a string and text type, respectively. The command generates the following files:
- A
message.rb
file that serves as the message model. - A migration file that contains instructions for creating a
messages
table in the database. The file's name is a concatenation of the timestamp for when the file was created and the model name. E.g.20220112205345_create_messages.rb
.
Next, you'll edit the message model and add some validation rules. Rails provides validation rules that help define the valid state of model objects. These validation rules are checked before records are saved onto the database. Open the message model located at app/models/message.rb
and add the following lines of code to the file:
class Message < ApplicationRecord
validates :title, :body, presence: true
end
The code above validates the presence of a title and body in every message object and prevents saving message objects where these properties are missing. Save the file after making the changes.
Before message objects can be saved to the database, you need to run the migration file that was generated when creating the message model. The migration file located at db/migrate/<timestamp>_create_messages.rb
contains code to create a messages
table with a title
, body
and timestamps columns (created_at
and updated_at
). To run the migration, run the command below in your terminal window:
rails db:migrate
The command above is Rails' database migrate command. Running this command executes the code in the migration file. When ran successfully, it returns an output similar to the following:
== 20220112205345 CreateMessages: migrating ===================================
-- create_table(:messages)
-> 0.0310s
== 20220112205345 CreateMessages: migrated (0.0312s) ==========================
With the messages
table now created, it can be populated with predefined messages. Rails offers the ability to seed the database with predefined records using a seeds file. The Rails' seeds file is located at db/seed.rb
and is triggered by the database seed command.
To create some demo messages for the application, open the seeds file located at db/seed.rb
in your favorite text editor and add the following code to the file:
p 'Running seeds'
10.times do |i|
Message.create!(
title: "Message #{i}",
body: "Prow scuttle parrel provost Sail ho shrouds spirits boom mizzenmast yardarm. Pinnace holystone mizzenmast quarter crow's nest nipperkin grog yardarm hempen halter furl. Swab barque interloper chantey doubloon starboard grog black jack gangway rutters.
Deadlights jack lad schooner scallywag dance the hempen jig carouser broadside cable strike colors. Bring a spring upon her cable holystone blow the man down spanker Shiver me timbers to go on account lookout wherry doubloon chase. Belay yo-ho-ho keelhaul squiffy black spot yardarm spyglass sheet transom heave to.
Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway. Case shot Shiver me timbers gangplank crack Jennys tea cup ballast Blimey lee snow crow's nest rutters. Fluke jib scourge of the seven seas boatswain schooner gaff booty Jack Tar transom spirits."
)
end
p 'Seeds run successfully'
The code above runs a loop ten times and creates a message record using the predefined values. To run the seeds, run the following code in your terminal window:
rails db:seed
The database seed command above runs the code in the seeds file and populates the database with ten records. When ran successfully, it returns the following output:
"Running seeds"
"Seeds run successfully"
To see the number of messages in the database, open the rails console by running rails console
in your terminal window. Running that command will return an irb
prompt like this:
Loading development environment (Rails 6.1.4.4)
irb(main):001:0>
In the prompt, run Message.count
to see the number of message records in the database. The command should return the following:
(8.8ms) SELECT COUNT(*) FROM "messages"
=> 10
The query result shows there are ten message records in the database. Type exit
to quit the irb
prompt at any time.
With the messages in place, add the logic for viewing all and one message. Rails applications can create, read, update and delete records in the database using Active Record. Open the app/controllers/secret_messages_controller.rb
file in your text editor and update the index
and show
methods with the following code:
def index
@messages = Message.all
end
def show
@message = Message.find(params[:id])
end
In the index
action, Active Record's all
method is used to fetch all the message records in the database, and the result is assigned to a @messages
instance variable. In the show
action, Active Record's find
method is used to find the message whose id
matches the id
in the request params and is assigned to the @message
instance variable. The instance variables ensure the messages are accessible in the view.
The last step towards viewing the existing messages in the app is to display them in their respective view pages. The index
view will display a list of all messages in the database, while the show page will display individual messages. First, open the index
view located at app/views/secret_messages/index.html.erb
and replace the code in it with the following:
<h1>Secret Messages</h1>
<% @messages.each do |message| %>
<h4> <%= link_to message.title, secret_message_path(id: message.id) %> </h4>
<% end %>
The code in the index
view above accesses the @messages
instance variable created in the index
action of the SecretMessages
controller and loops over it to render each message's title in a link that links to the show page for each message. Lastly, open the show
view located at app/views/secret_messages/show.html.erb
and replace the code in it with the code below:
<h1><%= @message.title %></h1>
<p><%= @message.body %></p>
<%= link_to 'Back', secret_messages_path %>
The code in the show
view above accesses the @message
instance variable declared in the show
action of the SecretMessages
controller and displays the message's title, body and a link to go back to the index
view.
With the code to view the messages in place, restart your Rails server and visit http://localhost:3000/secret_messages
in your browser. You will see a page with message titles like the one below:
Clicking on any of the message titles will open up a page like this:
In this section, you created a model to store messages, added a few messages to the database and displayed them in the app. In the next section, you will hide these messages behind an authentication wall.
Integrate Authentication to the Application with Devise
Devise is an authentication solution for Rails. It offers a suite of authentication features that can be easily plugged into any Rails application. In this section, you will add authentication to the demo application with Devise.
To set up Devise, start by adding it to the Gemfile. To do this, open the Gemfile
located in the application's root folder and add the following line of code to the end of the file:
gem 'devise'
Next, in your terminal window, run bundle install
to install the gem. Devise provides an install generator, which configures it for use in a Rails application when you run it. In your terminal window, run the command below to configure Devise:
rails generate devise:install
Running the command above creates the following files:
- A
devise.rb
file located atconfig/initializers/devise.rb
with Devise configuration options. - An English translation file named
devise.yml
. It is located atconfig/locales/devise.en.yml
. The command also returns an output with some compulsory and optional instructions. One of the compulsory instructions is to set a default URL option for Devise mailer. To do this for the development environment, in your text editor, open thedevelopment.rb
file located atconfig/environments/development.rb
and add the following code to the file:
Rails.application.configure do
...
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
end
Devise uses the default URL options set in the code above to generate URLs in emails. Lastly, add the markup below to the application.html.erb file immediately after the opening <body>
tag:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
The code above renders error and success messages generated by Devise.
Messages in the demo application should be visible to only authenticated users. Devise offers a model generator command that creates a Rails model that can be used for authentication. Next, you will create a User
model using Devise's model generator command. In your terminal window, run the command below to create a user model with Devise:
rails generate devise User
The command above generates two files; a user model located at app/models/user.rb
and a migration file for creating the users table. The command also updates the route files with user routes for Devise. The user model located at app/models/user.rb
contains five Devise modules. They include:
database_authenticatable
for hashing and storing passwords.registerable
for handling the creation, editing and deletion of user accounts.recoverable
for enabling password reset functionality.rememberable
for generating and clearing tokens for remembering users.validatable
for enabling email and password validation.
The authentication functionalities provided by the five Devise modules in the user model are already implemented and are functional as soon as the users table migration is run. To run the migration, run rails db:migrate
in your terminal window. You should get the following output if successful:
== 20220120200559 DeviseCreateUsers: migrating ================================
-- create_table(:users)
-> 0.0119s
-- add_index(:users, :email, {:unique=>true})
-> 0.0027s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0020s
== 20220120200559 DeviseCreateUsers: migrated (0.0168s) =======================
Devise offers authentication helper functions for Rails controllers and views. They include:
authenticate_user!
: This helper function denies controller access to unauthenticated users.user_signed_in?
: This returns a boolean that indicates whether or not a user is signed in.current_user
: This helper returns the currently signed-in user's object.user_session
: This helper returns information about the current user session.
Take note that the Devise helper function names depend on whatever your Devise model is named. This means if your Devise model is Admin
instead of User
, the helper function names will be authenticate_admin!
, admin_signed_in?
, current_admin
and admin_session
.
With the users migration now run, you'll restrict access to the index
and show
actions of the SecretMessages
controller using the authenticate_user!
helper. Open the SecretMessages
controller and add the following line of code to it:
class SecretMessagesController < ApplicationController
before_action :authenticate_user!, except: %i[home]
...
end
In the code above, the authenticate_user!
helper is used in a before_action
in the SecretMessages
controller. It ensures that unauthenticated users only have access to the home action in the controller. To test this out, restart your Rails server and try viewing all the messages in the app by visiting http://localhost:3000/secret_messages
in your browser. You will be redirected to a login page like the one below with a message indicating that you need to be authenticated to continue:
The login page is created by Devise and contains links to other authentication pages like sign up and password reset. To view the messages, go ahead and create an account and log in.
Next, you'll add a login/logout link to the app based on whether or not a user is logged in. In your editor, open the application.html.erb
file located at app/views/layouts/application.html.erb
and add the following code after the opening <body>
tag:
<body>
<% if user_signed_in? %>
<p>Welcome <%= current_user.email %></p>
<%= link_to 'Logout', destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to 'Login', new_user_session_path %>
<%= link_to 'Sign up', new_user_registration_path %>
<% end %>
...
</body>
In the code above, a conditional statement is used to display either a logout link or a login and sign up link based on the value of the user_signed_in?
helper. If a user is signed in, the current_user
helper is used to display a welcome message that includes the user's email address.
Reloading the application in your browser should show the login and signup or logout text based on your authentication state.
Deploying the Rails application to Koyeb
In this section, you will deploy your Rails application to Koyeb. Koyeb is a serverless platform for deploying applications across clouds and edges. It offers application deployment using pre-built Docker containers or native code using git. In this guide, we are going to deploy our app using the git-driven deployment method.
Start by creating a repository for your Rails application on GitHub. Next, run the commands below in your terminal window to push the application code to your GitHub repo:
git add --all
git commit -m "Complete application with authentication."
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_REPOSITORY_NAME>.git
git branch -M main
git push -u origin main
Running the commands above moves the application code from your local development to GitHub. Next, head over to ElephantSQL to create a Postgres database using the following steps:
- While logged in and on the instances page, click the "Create a new instance" button.
- Enter a name and click the "Select region button".
- On the next page, click the "review" button and then click the "Create instance button on the next page".
- Select your newly created instance and copy your database URL. Store it in a safe place for use later.
On your Koyeb control panel, go to the Secrets tab and create a new Secret. Enter DATABASE_URL
as the secret name and paste your database URL as the value. Next, go to the Overview tab and click the Create Web Service button to begin:
- Select GitHub as your deployment method.
- In the repositories list, select your application code repository.
- In the Builder section, click the Override toggle associated with the Run command and enter
rails db:migrate && rails db:seed && rails server
in the field. - In the Environment variables section, click Add variable. Select the Secret type, enter
DATABASE_URL
as the key and select theDATABASE_URL
secret created previously as the value. - Choose a name for your App and Service, for example
rails-auth
, and click Deploy.
Clicking the Create App button builds and deploys your app and redirects you to a deployment page where you can monitor the deployment. Once the deployment is complete and all necessary health checks have been passed, you can click your public URL to view your application.
Your application now benefits from built-in continuous deployment, global load balancing, end-to-end encryption, its own private network with service mesh and discovery, autohealing, and more.
Conclusion
In this tutorial, you created a Rails application for displaying messages. You also implemented authentication in the application using the Devise gem. Devise offers even more authentication features than what was covered in this tutorial. You can check out their wiki to learn more about Devise.
Since we deployed the application on Koyeb using git-driven deployment, each change you push to your repository will automatically trigger a new build and deployment on the Koyeb Serverless Platform. Your changes will go live as soon as the deployment passes all necessary health checks. In case of a failure during deployment, Koyeb maintains the latest working deployment in production to ensure your application is always up and running.
If you would like to look at the code for the demo application, you can find it here.
If you would like to read more Koyeb tutorials, checkout out our tutorials collection. Have an idea for a tutorial you'd like us to cover? Let us know by joining the conversation over on the Koyeb community platform!