How To Create A Ruby On Rails API With Scaffold - Full 10 Step Guide In 5 Minutes
Tags: rails, react, webdev, ruby
Published: 2022-10-19 19:20:28

<h1>This guide is multi-use, designed for passing a code challenge but could be useful for jumping into a new codebase, or refreshing your knowledge of ruby on rails, or getting a brief overview of how an MVC (Model View Controller) framework works!</h1>

<h2>
1) Commands
</h2>

```
$ bundle install
```
```
$ npm install --prefix client
```
<h2>
2) Generators
</h2>
```
$ rails g scaffold lowercase_snake_formatted_name attr_default_string attr_of_type:datatype model_that_has_many_of_these:belongs_to --no-test-framework
```
Lowercase snake is is the only format that will turn test_model into TestModel, TestModels, has_many :test_models, etc in all the right places.

Pay attention to using belongs_to relationships in this generator as it will create the foreign key tables. We won’t need to use has_many in this line ever because of the nature of where foreign keys live.

If you make a mistake . . .
```
$ rails destroy scaffold scaffold_to_destroy
```
<h2>
3) Fill out model relationships
</h2>
belongs_to will be created automatically
has_many will be created like so . . .
```ruby
    has_many :signups, dependent: :destroy
    has_many :campers, through: :signups
```

This is a good time to consider dependent: :destroy if applicable.

<h2>
4) Fill out model validations
</h2>
Here are some common ones . . .
```ruby
    validates :name, presence: true
    validates :age, :inclusion => 8..18
    validates :email, :uniqueness: true
```

<h2>
5) Seed
</h2>
```
$ rails db:migrate db:seed
```
<h2>
6) routes.rb - Fill it out correctly
</h2>
If all routes are required we use
```ruby
resources :model
```

Otherwise the following paths correspond to the following only: array symbols
GET /models => [ . . . , :index]
GET /models/:id => [ . . . , :show]
POST /models => [ . . . , :create]
PATCH /models/:id => [ . . . , :update]
DELETE /models/:id => [ . . . , :destroy]

Altogether we end up with something like . . .
```ruby
resources :models, only: [:index, :show, :create]
```

~) As you do the following be mindful of what routes you need, or perhaps do ALL of the steps if unsure . . .

<h2>
7) Clean Up Tasks
</h2>
In any controllers that take params . . .
```ruby
params.require(:model).permit(:attr1, :attr2)
```
…becomes . . .
```ruby
params.permit(:attr1, :attr2)
```
…to be safe we can also add to the TOP of each of these controllers . . .
```ruby
wrap_parameters format: []
```

We can also deal with RecordNotFound errors that will be triggered by any of the above methods a single time in application_controller.rb
We add the following . . .
```ruby
  rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response

  private

  def render_not_found_response(exception)
    render json: { error: "#{exception.model} not found" }, status: :not_found
  end
```

In Controllers, add bang operator ! to create and update methods if they are going to be used.
```ruby
def create
  @model = Model.new(model_params)

  if @model.save
    render json: @model, status: :created, location: @model
  else
    render json: @model.errors, status: :unprocessable_entity
  end
end
```
…becomes . . .
```ruby
def create
  @model = Model.create!(model_params)
  render json: @model, status: :created
end
```
Likewise the update methods are adjusted the same way.
```ruby
def update
  if @model.update(model_params)
    render json: @model
  else
    render json: @model.errors, status: :unprocessable_entity
  end
end
```
…becomes as simple as . . .
```ruby
def update
  @model.update!(model_params)
  render json: @model, status: :accepted
end
```

Add head :no_content to destroy routes
```ruby
def destroy
  @activity.destroy
end
```
…becomes . . .
```ruby
def destroy
  @activity.destroy
  head :no_content
end
```

<h2>
8) Add unprocessable_entity errors where needed
</h2>
If we have a POST or UPDATE path in use for a model (create or update methods) we also must add to our private methods inside the controller PRIVATE section . . .
```ruby
def render_unprocessable_entity_response(invalid)
  render json: { errors: invalid.record.errors.full_messages }, status: :unprocessable_entity
end
```
…since these methods take parameters (strong) and at the TOP of this controller we must add . . .
```ruby
rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
```
This must be in place in each controller where POST or UPDATE are in use.

<h2>
9) Special Concerns
</h2>
The “Instead Of” case: When a user signs up for a signup instead of returning the signup we want to return what they signed up for. This is very easy we do the following for example . . .
```ruby
def create
  @signup = Signup.create!(signup_params)
  render json: @signup, status: :created
end
```
…becomes . . .
```ruby
def create
  @signup = Signup.create!(signup_params)
  render json: @signup.activity, status: :created
end
```
<small>@signup => @signup.activity</small>

We can also solve this by creating a new serializer, but we don’t always need to. If it’s a wickedly simple replace the instance with an associated instance that is formatted using the default serializer we can do it like the above.

In other words we are returning the default-formatted associated model instance of interest instead of the one being operated on.

Return All Associated Instances case: I have a user with many tickets, when I show method/route on this user I want their tickets to be returned as children. As long as model relationships and db are associated properly this is as simple as adding to the UserSerializer the following line . . .
has_many :tickets

The tickets will be formatted using the default TicketSerializer.

It’s good to note here that if we have a many-to-many relationship like a doctor who has many patients through appointments we can return the patients the same way using directly a has_many :patients and the serializer should know to jump over the join table.

But Only Sometimes case: What if I don’t want the tickets to come through on an index method/route when we see all the users and instead only when we see just one? Instead of modifying the main serializer, in this case UserSerializer we can make a new one . . .
```
$ rails g serializer user_show_tickets
```
Since this serializer wasn’t scaffolded we need to make sure we add the appropriate main attributes at the top. They can be copied from the regular UserSerializer as long as we want them . . .
```ruby
attributes :id, :name, :age, :email
```

Then we add the has_many here instead of in the UserSerializer. Altogether it looks something like . . .
```ruby
class UserShowTicketsSerializer < ActiveModel::Serializer
  attributes :id, :name, :age, :email
  has_many :tickets
end
```

Finally, we specify that this method/route uses this serializer inside the controller where it is declared. . .
```ruby
def show
  render json: @user, serializer: UserShowTicketsSerializer
end
```

We can specify a serializer to do anything on a specific route this way, however it’s important to note that inside a serializer we have access to the instance by calling it object if we want to access specific properties of it.

<center>-----------------------------------------------</center>

<h2>
10) Troubleshooting
</h2>
If we are using @model instance methods or a set/find private method throughout our controllers this is a good clue that we should include a line like the following at the top of the controller if we want to use that variable directly. Scaffolding takes care of this automatically, but in case it’s helpful . . . 
```
before_action :set_instance, only: [:show, :update, :destroy]
```
Here we are saying we need to set this variable when we use the following method-routes (the ones that depend on using that variable).

If we are using the server and npm to test instead of using tests it might become critical to clear our database on re-testing in case bad data was passed into the db along the way of getting our code working . . . we can add the following to our seeds.rb . . . (above the create invocations)
```
puts "Clearing database"
ModelOne.destroy_all
ModelTwo.destroy_all
ModelThree.destroy_all
```

Now, our
```
$ rails db:seed
```
works a lot like a replant.

BONUS) Comment out or remove routes in the controllers that aren’t being used.

Hope this is helpful, let me know if I missed anything or made errors!
-Elliot/Big Sis