DEV Community

Cover image for The Basics of Building a Functional API Using Active Record and Sinatra
Lukas Mills
Lukas Mills

Posted on

The Basics of Building a Functional API Using Active Record and Sinatra

Quite often when building projects during Phase 1 and Phase 2 I ran into a lot of API issues where not a single API that I could find had all of the correct data that I wanted to use for my website. Now, that I have learned Sinatra, and Active Record I can build my own API so I can have all of the data I've ever dreamed of using. Hopefully, after you're done reading this you'll be able to build your own API as well!

Starter Code:

To get started building your own API let's first get some starter code! Fork and clone this down to your machine. https://github.com/learn-co-curriculum/phase-3-sinatra-react-project

For this example today we will be building an API for a movie review website!

Bundle Install

Open up and new terminal and type this into your console:

bundle install
Enter fullscreen mode Exit fullscreen mode

What this does is it installs every single Gem in our Gemfile to help us create our API. Some of the more important Gem's include: sinatra, rake, sqlite3, faker, and activerecord-reset-pk-sequence.

Image description

Creating our Models

Go into your App >> Models folder, and create new files called movie.rb, review.rb, and user.rb. Creating our models not only gives us the opportunity to create customs methods to do whatever we want with our data, but it allows us to get proper associations (relationships) which will play a vital role in building our database.
Image description

After you have created our models, let's give them some class names!

movie.rb:

class Movie < ActiveRecord::Base

end
Enter fullscreen mode Exit fullscreen mode

review.rb:

class Review < ActiveRecord::Base

end
Enter fullscreen mode Exit fullscreen mode

user.rb:

class User < ActiveRecord::Base

end
Enter fullscreen mode Exit fullscreen mode

You might be wondering what does "ActiveRecord::Base" do? This essentially provide an interface and binding between the tables in our relational database and the Ruby program code that manipulates database record.

Giving Our Models Associations With One Another:

Before we jump into the code it's important to think about our domain model. A domain model is essentially, for a lack of a better term a way of "whiteboarding" our associations between models.

Our domain model: movie —--<reviews>-------user
Looking at our domain model we can begin to think about their associations between one another in pseudocode.
A movie has many reviews
A movie has many users, through reviews
A user has many reviews
A user has many movies, through reviews
A review belongs to a movie
A review belongs to a user

Now let's jump into our code and let's start creating those associations!

movie.rb:

class Movie < ActiveRecord::Base
    has_many :reviews
    has_many :users, through: :reviews
end

Enter fullscreen mode Exit fullscreen mode

user.rb:

class User < ActiveRecord::Base
    has_many :reviews
    has_many :movies, through: :reviews
end

Enter fullscreen mode Exit fullscreen mode

review.rb:

class Review < ActiveRecord::Base
    belongs_to :movie
    belongs_to :user 
end

Enter fullscreen mode Exit fullscreen mode

If you haven't been able to tell already, we just put into our code what we wrote out in pseudocode, just with some syntax!

Creating migrations:

The next step of our journey is creating our migrations! Migrations give us a way of essentially creating columns and keys in our database!

Open up a new terminal and type these lines in one at a time and hit enter!

rake db:create_migration NAME="create_movies"

rake db:create_migration NAME="create_users"

rake db:create_migration NAME="create_reviews"
Enter fullscreen mode Exit fullscreen mode

After you've created these migrations go into your db >> migrate folder and you should see something similar to what I have:

Image description

Great! We've created these migrations for our tables, but how do we create new columns for our table?

create_movies.rb:

class CreateMovies < ActiveRecord::Migration[6.1]
  def change
    create_table :movies do |t|
      t.string :title
      t.string :image
      t.integer :release_date
      t.string :genre
    end
  end
end

Enter fullscreen mode Exit fullscreen mode

WOAH! That's a lot of information to take in let's break it down step by step:
class CreateMovies: Gives us a class name of our migration
ActiveRecord::Migration[6.1]: Some rake magic that allows us to create migrations for our tables.
def change: Creating a method
create_table :movies: Creates a table with the name of movies.
t.string: Allows us to apply a datatype to the column we are creating.
:title: Creates a name for our data that we want represented in our table or frontend. Here so one of our movie titles will be: 'Interstellar'

Now let's go ahead and create every migration for each model:

create_users.rb:

class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      t.string :name
    end
  end
end

Enter fullscreen mode Exit fullscreen mode

create_reviews.rb:

class CreateReviews < ActiveRecord::Migration[6.1]
  def change
    create_table :reviews do |t|
      t.integer :movie_id
      t.integer :user_id
      t.string :comment
      t.integer :rating

    end
  end
end


Enter fullscreen mode Exit fullscreen mode

Looking at our create_reviews.rb migration can you tell what the difference is?

When we refer back to our domain model and our pseudocode movie —--<reviews>-------user we can see that our reviews belong to a movie and a user. By creating columns for our movie_id and our user_id we are putting in the final puzzle piece to create associations between our models.

After creating our migrations let's actually migrate them to our database!

In your terminal type this:

rake db:migrate
Enter fullscreen mode Exit fullscreen mode

Great we have our columns appropriately applied to our tables, but now how can we add data to them?

Seeding our data:

For our API to display any information we have to give it some type of value, one of the easiest ways to do this is to seed our data! Looking at our file directory we will see a starter seeds.rb file.

Once you are in there let's create our very first movie!

m1 = Movie.create( 
    title: 'Harry Potter and the Goblet of Fire',
    image: 'https://m.media-amazon.com/images/I/71opdcUCGjL.jpg',
    release_date: 2005,
    genre: 'Fantasy'
)
Enter fullscreen mode Exit fullscreen mode

Looking at this, you can already get a sense of what is going on here! We are creating an instance of a movie and giving our keys like title and image some value in our database! But what about users, and reviews?

Faker:

In this case, Faker is a ruby gem that allows us to randomly generate names for our Users so we don't have to continuously have to create new instances of our Users ourselves.

Users:

5.times do 
    User.create(name: Faker::Name.name)
Enter fullscreen mode Exit fullscreen mode

This is creating 5 Users, and each giving them a randomly generate name!

Reviews:

r1 = Review.create(comment:"I liked this film very much. It is much darker than the previous outings, but not as faithful to the source material. The only thing I didn't like so much about the book, was the subplot about Hermione trying to help house elves. It was cute, but interfered too much with the dark overtones of the narratives. The film looks dazzling, especially the ballroom scene. Speaking of that scene, I adored that dress that Hermione was wearing, Emma Watson looked unrecognisable in that scene.Also the music by Patrick Doyle this time was beautiful." , rating: 5 , user_id: User.ids.sample , movie_id: m1.id)

Enter fullscreen mode Exit fullscreen mode

Unfortunately for us there isn't a Faker for creating reviews, so we must hard code our own reviews!

After you have done all of the steps go into your terminal and type in
rake db:seed
We now have all of the essential data to fetch on the frontend! But how are we able to tie in this data and use a fetch call to display it on the front end?

Controllers

Controllers act as a middle man that allows us to create custom routes when fetching on the frontend.

The first step we must do is go into our app >> controllers >> application_controller.rb file and create a class that inherits Sinatra.

class ApplicationController < Sinatra::Base
set :default_content_type, 'application/json'

end

Enter fullscreen mode Exit fullscreen mode

Now let's create our custom routes for the data we want displayed!

get "/movies" do
    movies = Movie.all
    movies.to_json
  end

Enter fullscreen mode Exit fullscreen mode

Now what does all of this do?

/:specifies the route path we want to create.
movies = Movie.all: creates a variable holding all of our Movie data.
movies.to_json: Converts all of the data we have on movies to json.

Now what if we wanted to include our users and our reviews with our movies?

get "/movies" do
    movies = Movie.all
    movies.to_json(include: { reviews: {include: :user} }) 
  end
Enter fullscreen mode Exit fullscreen mode

Now if you start your rake server in your terminal with rake server and type http://localhost:9292/movies in your browser, you should we something similar to what I have!

Image description

You have just built your first local API! Congragulations!

Top comments (0)