How to create a Rails API From SCRATCH

Why create an API from scratch? After all, there is a rails scaffold. A generator is a fast way to create rails applications within minutes. The downside with a generator is that you may end up with extra files, data you don’t need, adding unnecessary layers of complexity taking memory space. Also, your API will probably need to be customized. For this tutorial, I will be using one of my boot camp projects as a test case.

I Planning

Test Case Example

My user story is to create an Open house customer relationship management. The user “real estate agent” will enter “guests” personal information, associated with houses visited, during open houses for follow-up, and ultimately add new clients to his pipeline.

II Generate Rails API

rails new my-api — api — database=postgresql

The command will create the model, route, controller files necessary to build your API. By using Postgresql instead of the default SQLite you will be able to deploy on Heroku.

III Set up Cors

A/ Add Cors gem to your Gemfile

gem ‘rack-cors’bundle update

B/Add the code below to your cors.rb file

//config/initializers/cors.rbRails.application.config.middleware.insert_before 0, Rack::Cors doallow doorigins ‘*’resource ‘*’,headers: :any,methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end

C/Rails 6 configuration

//config/environments/development.rb config.hosts = nil

or

config.hosts << “your url”

IV Build Vertically

A/ Build the Table and Migrate

Build tables

rails generate migration CreateHousesrails generate migration CreateGuests

The command will create a file with a timestamp. In this file, you can add your attributes and data type manually or generate synchronously with the table creation.

House table

//20201027175554_create_houses.rbclass CreateHouses < ActiveRecord::Migration[6.0]
def change
create_table :houses do |t|t.string :house_addresst.timestampsendendend

Guest table

//20201027182820_create_guests.rbclass CreateGuests < ActiveRecord::Migration[6.0]def changecreate_table :guests do |t|t.string :namet.string :phone_numbert.string :guest_addresst.string :emailt.string :time_linet.string :commentt.references :house, null:false, foreign_key: truet.timestampsendendend

I added a foreign key “house_id” to the guest table to be able to associate guests with a target house.

Migrate

rake db:migrate

The command will create a new schema file. If you see a mistake in your table and column you can undo it by using “rake db: rollback”

//db/schema.rbActiveRecord::Schema.define(version: 2020_10_27_182820) docreate_table “guests”, force: :cascade do |t|t.string “name”t.string “phone_number”t.string “guest_address”t.string “email”t.string “time_line”t.string “comment”t.integer “house_id”, null: falset.datetime “created_at”, precision: 6, null: falset.datetime “updated_at”, precision: 6, null: falset.index [“house_id”], name: “index_guests_on_house_id”endcreate_table “houses”, force: :cascade do |t|t.string “house_address”t.datetime “created_at”, precision: 6, null: falset.datetime “updated_at”, precision: 6, null: falseendadd_foreign_key “guests”, “houses”end

B/Create seeds Data

rake db:seed

Create manually or use faker gem

//seeds.rbhouse = House.create(house_address: “testhouseadress”)guest = Guest.create(name: “nametest”, phone_number: “phonenumbertest”, guest_address: “guestsaddresstest”,email: “emailtest”,time_line:”timelinetest”, comment:”commenttest”, house_id:2)guest_two = Guest.create(name: “nametestwo”, phone_number: “phonenumbertestwo”, guest_address: “guestsaddresstesttwo”,email: “emailtesttwo”,time_line:”timelinetesttwo”, comment:”commenttesttwo”, house_id:3)

C/ Create models, controller, validations, routes

House Controller and model

//controllers/api/v1/houses_controller.rbclass Api::V1::HousesController < ApplicationControllerdef index
@houses = House.all
render json: @houses
end
def create
@house = House.new(house_params)
if @house.save
render json: @house
else
render json: {error: ‘Error creating house’}
end
end
def show
@house = House.find(params[:id])
render json: @house
end
def destroy
@house = House.find(params[:id])
@house.destroy
end
privatedef house_params
params.require(:house).permit(:house_address)
end
end
//models/house.rbclass House < ApplicationRecordhas_many :guestsvalidates :house_address, presence: trueend

In the house model, each house is associate with multiple guests. The house_address attribute is required to create a house object.

ROUTES

To render different URL’s, you need routes. You can customize your route name. In this case, the URL will be localhost:3000/api/v1/houses. Verify your routes with “rails routes”. In this test case, because I created a nested object, I nested the routes as well.

Rails.application.routes.draw donamespace :api donamespace :v1 doresources :houses doresources :guestsendendendend

Guest controller and model

//app/models/guest.rbclass Guest < ApplicationRecord    
belongs_to :house
validates :name, :phone_number, :email, presence: true
end

The guest object belongs to the house object. A guest can't be created without a house_id. Also, the form will require the inputs of attributes to create a new guest object.

//controllers/api/v1/guests_controller.rbclass Api::V1::GuestsController < ApplicationControllerbefore_action :set_houserequire ‘pry’def index
@guests = Guest.all
render json: @guests
end
def show
@guest = Guest.find(params[:id])
render json: @guest
end
def create
@guest = @house.guests.create(guest_params)
if @guest.save
render json: @house
else
render json: {error: ‘Error creating guest’}
end
end
def destroy
@guest = Guest.find(params[“id”])
@house = House.find(@guest.house_id)
@guest.destroy
render json: @house
end
privatedef set_house@house = House.find(params[:house_id])enddef guest_paramsparams.require(:guest).permit(:name, :phone_number, :guest_address, :email, :time_line, :comment, :house_id)endend

I created a private set_house method to require to have a house_id associate with a guest to create a guest and destroy a guest to avoid orphan data.

Result

Rails S 

Voila! See JSON data at localhost3000/api/v1/houses