How to Deploy a Rails Application and Add Authentication using Devise
Tags: ruby, rails, tutorial, webdev
Published: 2022-02-09 17:25:44

## 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](https://customer.elephantsql.com/login) account to get a PostgreSQL database.
- A [Koyeb](https://app.koyeb.com/) account to deploy the demo application.

## Steps

The steps to implementing authentication in a Rails application and deploying it to Production on Koyeb include:

1. [Creating a new Rails application](#creating-a-new-rails-application).
2. [Setting up the database](#setting-up-the-database).
3. [Creating the application homepage](#creating-the-homepage).
4. [Adding messages to the application](#adding-messages-to-the-application).
5. [Integrate Authentication to the application with Devise](#integrate-authentication-to-the-application-with-devise).
6. [Deploying the Rails application to Koyeb](#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:

```bash
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:

```bash
├─ 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:

```bash
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:

```bash
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.

![Rails welcome page](https://i.imgur.com/6O54oUI.png)

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:

```yml
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:

```bash
rails db:prepare
```

This command creates a development and test database and returns the following output when run successfully:

```bash
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:

```bash
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:

```diff-ruby
- get 'secret_messages/home'
+ root 'secret_messages#home'
- get 'secret_messages/index'
+ get '/secret_messages', to: 'secret_messages#index'
- get 'secret_messages/show'
+ get '/secret_message/:id', to: 'secret_messages#show', as: :secret_message
```

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](https://simplecss.org/) 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:

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

```html
<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:


![Application homepage](https://i.imgur.com/tmKGtJ3.png)


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:

```bash
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:

```ruby
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:

```bash
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:

```bash
== 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:

```ruby
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:

```bash
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:

```bash
"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:

```bash
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:

```bash
(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:

```ruby
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:

```ruby
<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:
```ruby
<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:
![Messages index page](https://i.imgur.com/ZbBDmiA.png)
Clicking on any of the message titles will open up a page like this:
![Messages show page](https://i.imgur.com/pdkW2Fm.png)
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:
```ruby
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:
```bash
rails generate devise:install
```
 Running the command above creates the following files:
- A `devise.rb` file located at `config/initializers/devise.rb` with Devise configuration options.
- An English translation file named `devise.yml`. It is located at `config/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 the `development.rb` file located at `config/environments/development.rb` and add the following code to the file:
```ruby
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:
```ruby
<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:

```bash
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:

```ruby
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:


![Devise login page](https://i.imgur.com/qWFiebm.png)


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:

```ruby
<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:

```bash
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.


![ElephantSQL Postgres instance showing database URL](https://i.imgur.com/r41GNCe.png)


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 App** button to go to the App creation page.

On the App creation page:

1. Select GitHub as your deployment method.
2. In the repositories list, select your application code repository.
3. Specify the branch you want to deploy. In this case, `main`.
4. Set the run command to let Koyeb know how to deploy your app: `rails db:migrate && rails db:seed && rails server`
5. In the environment variables section, click the add environment variable button.
6. Select the `Secret` type, enter `DATABASE_URL` as the key and select the `DATABASE_URL` secret created previously as the value.
7. Give your app a name. e.g. `rails-auth` and click the **Create App** button.


![Deploying app on Koyeb](https://i.imgur.com/UiYX6PH.png)


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](https://github.com/heartcombo/devise/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](https://github.com/koyeb/example-rails-devise). If you have any questions or suggestions to improve this guide, feel free to reach out to us on [Slack](https://slack.koyeb.com/).