Whipping Up a Rails API Real Quick
Tags: ruby, rails, webdev, tutorial
Published: 2020-10-05 08:23:07

In this blog, I attempt a concise outline of the basic steps to create a Ruby on Rails RESTful API.

> [jump to the code on GitHub](https://github.com/cooljasonmelton/quick-api)

# Tutorial
### Steps
* Create New Rails Project
* Plan Models / Relationships
* Generate Resources
* Create / Migrate database
* Set Up Controllers

### 1. Create New Rails Project

Assuming Rails is installed, enter the following command into your terminal replacing `<my-project>` with the project name.

```
rails new <my-project> --api --database=postgresql
```

> **Flags:** The `--api` flag will forgo some of the extra junk a full Rails app has that I do not need. The `--database` flag lets you specify the type of database you prefer. I chose PostgreSQL. If you do not include this flag, the default database will be SQLite.

![command line](https://dev-to-uploads.s3.amazonaws.com/i/nd23bn1tukmy2lw37r5w.png)

For demonstration, I’ll create an Evil Dog Catcher API.

Check out this dog catcher toy I found on Google images:

![dog catcher toys](https://dev-to-uploads.s3.amazonaws.com/i/5gjb42dsrw121k8q3588.gif)

What maladjusted kid wants to play with dog catcher toys? Yikes.

### 2. Plan Models / Relationships
While Rails installs, now is a great time to plan the models and relationships. Having a clear idea of these ahead of time saves a lot of headaches.

I'm going with two models, Catchers and Dogs. 

The relationships will be:
1. Catchers have many Dogs.
2. Dogs belong to Catchers.

![relationship diagram](https://dev-to-uploads.s3.amazonaws.com/i/spdrz1iuaass2ivi0uva.png)

### 3. Generate Resources

Next, I’ll generate resources. This command quickly sets up migrations, models, controllers, and routes for each model name.

```
rails generate resource <model> <attribute>:<type> <attribute>:<type>
```

> **Tips:** You can use `g` as shorthand for `generate`. Also, you can list another model’s name as an attribute with a type of `references` to automatically add a foreign key to that model.

These will be my resource commands:
```
rails g resource catcher name:string city:string
rails g resource dog name:string breed:string catcher:references
```

References gives me a foreign key for the Dog model, but I have to manually go into the Catcher model add `has_many :dogs`.

```ruby
class Dog < ApplicationRecord
  belongs_to :catcher # set up by catcher:references
end

class Catcher < ApplicationRecord
    has_many :dogs # add this line
end
```

### 4. Create / Migrate database

With all the files generated, it’s time to get the database going. First, I double check the migration files to make sure everything looks right.

```ruby
class CreateCatchers < ActiveRecord::Migration[6.0]
  def change
    create_table :catchers do |t|
      t.string :name
      t.string :city

      t.timestamps
    end
  end
end

class CreateDogs < ActiveRecord::Migration[6.0]
  def change
    create_table :dogs do |t|
      t.string :name
      t.string :breed
      t.references :catcher, null: false, foreign_key: true

      t.timestamps
    end
  end
end
```
> **Note:** Notice the line under `CreateDogs` that starts with `t.references...`. Because `null: false`, I will not be able to create a Dog unless it has the foreign key of a Catcher. If you want to be able to create models without this, set `null: true`.

Since everything looks right, I will now create and migrate my database by entering these commands into the terminal:

```
rails db:create
rails db:migrate
```

I double check my progress in the following steps:

1. In the terminal, `rails c` or `rails console` opens the console.

2. In the console, I create a Catcher instance, `Catcher.create(name: 'test')`, and a Dog instance, `Dog.create(name: ‘test’, catcher_id: 1)`. There should be no errors.

3. I test the relationships by entering `Catcher.find(1).dogs`. This finds the Catcher with an id of 1 and shows me if they have any dogs. I get:
```
<ActiveRecord::Associations::CollectionProxy [#<Dog id: 1, name: “test”, breed: nil, catcher_id: 1, created_at: “2020–10–04 06:39:15”, updated_at: “2020–10–04 06:39:15”>]>
```

Since there’s no errors, it’s time for the next step.

### 5. Set Up Controllers
For the final step, I set up a basic index controller and test it on the Rails server.

```ruby
class CatchersController < ApplicationController

    def index
        # grab an array of all catchers in db
        catchers = Catcher.all
        # render a json object of all catchers
        # include a sub array of their dogs with each catcher
        render json: catchers, include: :dogs 
    end

end
```

I run `rails s` or `rails server` in the terminal. I open `http://localhost:3000/catchers` in the browser and make sure everything is working.

![browser window](https://dev-to-uploads.s3.amazonaws.com/i/enw7ymtngn5m5ayj3lu3.png)

> **Tip:** For production purposes, you can open the CORS of this API to any website. Install the CORS gem by opening the Gemfile, uncommenting `gem ‘rack-cors’`, and run `bundle install`.

```ruby
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'
```

> Next, access the `config/application` file. Find the class `Application` and — without altering any of the default code — insert the following:
```ruby
config.middleware.insert_before 0, Rack::Cors do
    allow do
        origins '*'
        resource '*',
            :headers => :any,
            :methods => [:get, :post, :delete, :put, :patch, :options, :head],
            :max_age => 0
    end
end
```
> Remember to change the `origins` before deployment or this API will be available to anyone.

### Conclusion

At this point the basic API is up. The next steps will be to open up the CORS for whatever other site(s) will pull from it and set up the controllers to perform whatever actions I want the back-end to perform.

Hope this is helpful! Mostly I wanted a quick reference for setting up an API. I know Rails has a million aliases and shortcuts. If you have suggestions / corrections. Please comment or email me at `jason.melton2@gmail.com`.