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
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.
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.
After you have created our models, let's give them some class names!
movie.rb:
class Movie < ActiveRecord::Base
end
review.rb:
class Review < ActiveRecord::Base
end
user.rb:
class User < ActiveRecord::Base
end
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
user.rb:
class User < ActiveRecord::Base
has_many :reviews
has_many :movies, through: :reviews
end
review.rb:
class Review < ActiveRecord::Base
belongs_to :movie
belongs_to :user
end
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"
After you've created these migrations go into your db >> migrate folder and you should see something similar to what I have:
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
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
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
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
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'
)
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)
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)
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
Now let's create our custom routes for the data we want displayed!
get "/movies" do
movies = Movie.all
movies.to_json
end
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
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!
You have just built your first local API! Congragulations!
Top comments (0)