Lesson Weekend

Let's add some routes to our API and wire up the database. Once again, we'll keep it very simple. Add a quote.rb model to your models folder and make the following migration:

201703150339143_add_quotes_table.rb
class AddQuotesTable < ActiveRecord::Migration[5.0]
  def change
    create_table :quotes do |t|
      t.column :content, :string
      t.column :author, :string
    end
  end
end

We'll also need a model for the Quote class as well:

models/quote.rb
class Quote < ApplicationRecord
end

Now let's seed our database with the Faker gem. We can use Faker::Book.author to randomly generate authors but there's nothing for quotes. We'll have to use something else instead. Let's go with Faker::Movie, which includes movie quotations. Here's our seed file:

db/seeds.rb
class Seed

  def self.begin
    seed = Seed.new
    seed.generate_quotes
  end

  def generate_quotes
    20.times do |i|
      quote = Quote.create!(
        author: Faker::Book.author,
        content: Faker::Movie.quote
      )
      puts "Quote #{i}: Author is #{quote.author} and quotation is '#{quote.content}'."
    end
  end
end

Seed.begin

You'll notice some changes in this seed file. The process is different but the end result is the same. We create a Seed class, instantiate a seed, and then call our generate methods separately. It's just plain old Ruby. Writing the seed file this way makes it easy to have separate methods for each object we want to generate. We’ve also added a line at the end that will print all new quotes to the console as they’re being seeded.

Make sure to run rake db:seed once this file is complete.

Now let's add all the code we'll need in our controller:

controllers/quotes_controller.rb
class QuotesController < ApplicationController

  def index
    @quotes = Quote.all
    json_response(@quotes)
  end

  def show
    @quote = Quote.find(params[:id])
    json_response(@quote)
  end

  def create
    @quote = Quote.create(quote_params)
    json_response(@quote)
  end

  def update
    @quote = Quote.find(params[:id])
    @quote.update(quote_params)
  end

  def destroy
    @quote = Quote.find(params[:id])
    @quote.destroy
  end

  private
  def json_response(object, status = :ok)
    render json: object, status: status
  end

  def quote_params
    params.permit(:author, :content)
  end
end

We've added show, create, update and destroy routes. We don't need new or edit routes because we have no views.

The routes we have are very similar to those in a vanilla Rails application. We still have strong parameters and we still need to find an individual quote through a URL parameter before we show, update or delete it.

However, instead of using the .new and .save methods in our create route, we use create. The create method is just a combination of .new and .save. Since we aren't using a form, there's no need for a separate .new method to store form parameters. In a future lesson, we'll change this to a .create! bang method instead. The bang method will throw exceptions if needed, which is important if something goes wrong with an API call. We don't have a way to handle exceptions yet, but we'll deal with that soon.

Go ahead and test some API calls using Postman. Here's the routing you'll use:

GET http://localhost:3000/quotes/:id will show an individual quotation by :id.

POST http://localhost:3000/quotes will post a new quotation. You can add the author and content by clicking on "Body" in Postman just below the URL and passing in key-value pairs.

PUT http://localhost:3000/quotes/:id will update a quotation. Just as with the POST call, you'll pass in the parameters you want to update in the body by using key-value pairs.

DELETE http://localhost:3000/quotes/:id will delete a quotation with the corresponding :id.

You can confirm that your POST, PUT and DELETE methods are working by checking the corresponding records in the Rails console. Note that you'll get a 404 - Not Found error if you try to retrieve an :id that doesn't exist.

We now have working API calls that allow users to to access and change records in our database.

However, we need to clean up a few things. If we make a PUT or DELETE request, we'll get a 204 No Content response from our application even though both requests are successful. We need to provide a message to users to let them know what happened to their requests.

We also don't have a way to handle exceptions and we don't have testing. If we deploy our application, any user, malicious or not, could wreck havoc on our database, so it might be nice to add an authentication scheme.

We could also refactor our code more. For instance, what happens when we want to use our json_response method in other controllers? Currently, the method is private and can only be accessed in quote_controller.rb.

In other words, we still have work to do.

Sample API routes in controller

controllers/quote_controller.rb
class QuotesController < ApplicationController

  def index
    @quotes = Quote.all
    json_response(@quotes)
  end

  def show
    @quote = Quote.find(params[:id])
    json_response(@quote)
  end

  def create
    @quote = Quote.create(quote_params)
    json_response(@quote)
  end

  def update
    @quote = Quote.find(params[:id])
    @quote.update(quote_params)
  end

  def destroy
    @quote = Quote.find(params[:id])
    @quote.destroy
  end

  private
  def json_response(object, status = :ok)
    render json: object, status: status
  end

  def quote_params
    params.permit(:author, :content)
  end
end

Lesson 4 of 19
Last updated more than 3 months ago.