# Getting Started With The FriendlyID Gem In Rails 7

> See how the FriendlyID gem makes it simple to convert you URLs into something more useful and friendly with Rails 7.

- Canonical: https://dennisokeeffe.com/blog/2022-03-12-getting-started-with-the-friendly-id-gem-in-rails-7
- Published: 2022-03-12
- Updated: 2022-03-12
- Tags: ruby, ruby-on-rails

This post will crash-course through how you can use the [Friendly ID](https://github.com/norman/friendly_id) gem in order to create routes that are more user-friendly.

Source code can be found [here](https://github.com/okeeffed/demo-friendly-id).

## Prerequisites

1. Basic familiarity with setting up a [new Rails project](https://guides.rubyonrails.org/getting_started.html).

## Getting started

We will use Rails to initialize the project `demo-friendly-id`:

```s
# Create a new rails project
$ rails new demo-friendly-id
$ cd demo-friendly-id

# Generate controller and methods for our demonstration purposes
$ bin/rails g controller documents
$ bin/rails g model Document body:string title:string author:string
```

In the above commands, we also generated a basic model Document and a controller for those documents to illustrate the usage of Friendly ID.

First of all, let's demonstrate how things work without Friendly ID.

## Updating routes

In order for our `show` route for a resource to work, we will need to enable that endpoint:

```rb
Rails.application.routes.draw do
  resources :documents, only: [:show]
end
```

## Creating some seed data

Update seeds from inside of the `db/seeds.rb` file to create four documents.

```rb
Document.create([
	{title: "Star Wards", body: "Doing things", author: "Bill"},
	{title: "Connect 4", body: "Cool article", author: "Bob"},
	{title: "Star Wards", body: "Bob has an article with the same name as Bill!", author: "Bob"},
	{title: "Friends From Afar", body: "Friends are forever", author: "Jane"}
])
```

Note that we are purposefully creating two documents titled `Star Wards`. This will demonstrate how we can enforce uniqueness with Friendly ID later when there are naming conflicts for our nominated ID key.

## Update the controller

We now need to create our `#show` route handler in `app/models/document.rb`:

```rb
class DocumentsController < ApplicationController
  def show
    @document = Document.find(params[:id])
    render json: @document
  end
end
```

We will just return the resource as JSON for our demonstration.

## Seeing how the app currently works

At this point, our code is ready to run a basic app.

Create and seed database, then start the Rails server:

```s
# Database work
$ bin/rails db:create db:migrate db:seed

# Start the server
$ bin/rails s
```

With the server running, we can grab any of the resources with their corresponding ID. Since we are auto-incrementing, this will be 1, 2, 3 or 4.

```s
$ curl http://localhost:3000/documents/1
{
    "author": "Bill",
    "body": "Doing things",
    "created_at": "2022-03-12T00:28:48.391Z",
    "id": 1,
    "slug": "star-wards",
    "title": "Star Wards",
    "updated_at": "2022-03-12T00:28:48.391Z"
}
```

At the moment, our URLs are not very user friendly if we were show this data on the frontend to clients. We can resolve this problem with Friendly ID.

## Adding in Friendly ID

Add the following to your `Gemfile`:

```rb
gem 'friendly_id', '~> 5.4.0'
```

Run `bundle` in the command-line to install the gem.

## Updating our model

We need to update our model to know what to reference for the slug in the model.

Update `app/controllers/documents_controller.rb` to the following:

```rb
class Document < ApplicationRecord
  extend FriendlyId
  friendly_id :slug_candidates, use: :slugged

  private

  # Try building a slug based on the following fields in
  # increasing order of specificity.
  def slug_candidates
    [
      :title,
      %i[title author]
    ]
  end
end
```

The `slug_candidates` method that we have written can be used to generate a slug if there is a collision. This helps us maintain control over what happens when we use something like the document name which is not unique.

## Adding a slug to our document and generating the ID

Run the following to add a `slug` field to documents and migrate the changes.

```s
# Required migration for field in DB
$ bin/rails g migration AddSlugToDocuments slug:uniq
$ bin/rails generate friendly_id
$ bin/rails db:migrate
```

## Update the controller to use Friendly ID

Friendly ID adds the `friendly` property to our model which we can insert to handle the param we pass the slug or database ID value to.

```rb
class DocumentsController < ApplicationController
  def show
    @document = Document.friendly.find(params[:id])
    render json: @document
  end
end
```

At this point, let's reset and reseed the database in order to generate the slugs and store them in our database.

```s
# Reset, migrate and re-seed to generate the slugs
$ bin/rails db:reset db:migrate db:seed
```

## Testing our endpoint

Restart the server with `bin/rails s`

Now, both http GET `localhost:3000/documents/1` and `localhost:3000/documents/star-wards` will return the first document.

```s
$ curl http://localhost:3000/documents/1
{
    "author": "Bill",
    "body": "Doing things",
    "created_at": "2022-03-12T00:28:48.391Z",
    "id": 1,
    "slug": "star-wards",
    "title": "Star Wards",
    "updated_at": "2022-03-12T00:28:48.391Z"
}
$ curl http://localhost:3000/documents/star-wards
{
    "author": "Bill",
    "body": "Doing things",
    "created_at": "2022-03-12T00:28:48.391Z",
    "id": 1,
    "slug": "star-wards",
    "title": "Star Wards",
    "updated_at": "2022-03-12T00:28:48.391Z"
}
# An example of our colliding document name going to the #{title}-#{author} format
$ curl http://localhost:3000/documents/star-wards-bob
{
    "author": "Bob",
    "body": "Bob has an article with the same name as Bill!",
    "created_at": "2022-03-12T00:28:48.403Z",
    "id": 3,
    "slug": "star-wards-bob",
    "title": "Star Wards",
    "updated_at": "2022-03-12T00:28:48.403Z"
}
```

Awesome!

## Summary

Today's post demonstrated how to use the [Friendly ID](https://github.com/norman/friendly_id) gem in order to generate friendly slugs for our URLs.

## Resources and further reading

- [Rails getting started](https://guides.rubyonrails.org/getting_started.html)
- [Source code](https://github.com/okeeffed/demo-friendly-id)

_Photo credit: [`pawel_czerwinski`](https://unsplash.com/@pawel_czerwinski)_
