Simple Instagram app with Rails 6 API and React. Part 1

Final result

The Rails Part.

  1. generate an API
$ rails new instabox-api — api -T
git init
git checkout -b master add README.md
git commit 'first commit'
git checkout -b development add .
git commit -m "initial commit"
git checkout -b setup-models
gem ‘rspec-rails’, ‘~> 3.5’group :test do
gem ‘factory_bot_rails’, ‘~> 4.0’
gem ‘shoulda-matchers’, ‘~> 3.1’
gem ‘faker’
gem ‘database_cleaner’
end
bundle install
rails g rspec:install to setup rspec
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'database_cleaner'Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }ActiveRecord::Migration.maintain_test_schema!Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
RSpec.configure do |config|config.fixture_path = "#{::Rails.root}/spec/fixtures"config.use_transactional_fixtures = trueconfig.include FactoryBot::Syntax::Methodsconfig.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
DatabaseCleaner.strategy = :transaction
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
config.infer_spec_type_from_file_location!
config.include RequestSpecHelperconfig.filter_rails_from_backtrace!end
rails g model Picture img_link:string likes:integer liked:boolean created_by:string
rails db:migrate # this command creates the db schema.
spec > models > picture_spec.rbrequire ‘rails_helper’RSpec.describe Picture, type: :model do
# checks that the img link isn't empty before adding to db
it { should validate_presence_of(:img_link) }
it { should validate_presence_of(:created_by) }
end
rspec
app > models > picture.rbclass Picture < ApplicationRecord
validates_presence_of :img_link, :created_by
end
rspec
git add .
git commit -m "models done with tests"
git checkout development
git merge models
git checkout -b controllers
rails g controller Pictures index create show destroy
touch spec/factories/pictures.rb
spec > factories > pictures.rbFactoryBot.define do
factory :picture do
img_link 'imglink.io'
likes '10'
liked false
caption 'caption'
created_by 'creator'
end
end
require 'rails_helper'RSpec.describe 'Pictures API', type: :request do
# initialize test data
let!(:pictures) { create_list(:picture, 10) }
let(:picture_id) { pictures.first.id }
# Test suite for GET /pictures
describe 'GET /pictures' do
# make HTTP get request before each example
before { get '/pictures' }
it 'returns pictures' do
# Note `json` is a custom helper to parse JSON responses
expect(json).not_to be_empty
expect(json.size).to eq(10)
end
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
end
# Test suite for GET /pictures/:id
describe 'GET /pictures/:id' do
before { get "/pictures/#{picture_id}" }
context 'when the record exists' do
it 'returns the picture' do
expect(json).not_to be_empty
expect(json['id']).to eq(picture_id)
end
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
end
context 'when the record does not exist' do
let(:picture_id) { 100 }
it 'returns status code 404' do
expect(response).to have_http_status(404)
end
it 'returns a not found message' do
expect(response.body).to match(/Couldn't find picture/)
end
end
end
# Test suite for POST /pictures
describe 'POST /pictures' do
# valid payload
let(:valid_attributes) { { title: 'Learn Elm', created_by: '1' } }
context 'when the request is valid' do
before { post '/pictures', params: valid_attributes }
it 'creates a picture' do
expect(json['title']).to eq('Learn Elm')
end
it 'returns status code 201' do
expect(response).to have_http_status(201)
end
end
context 'when the request is invalid' do
before { post '/pictures', params: { title: 'Foobar' } }
it 'returns status code 422' do
expect(response).to have_http_status(422)
end
it 'returns a validation failure message' do
expect(response.body)
.to match(/Validation failed: Created by can't be blank/)
end
end
end
# Test suite for DELETE /pictures/:id
describe 'DELETE /pictures/:id' do
before { delete "/pictures/#{picture_id}" }
it 'returns status code 204' do
expect(response).to have_http_status(204)
end
end
end
mkdir spec/support && touch spec/support/request_spec_helper.rb
rspec
config > routes.rbRails.application.routes.draw do
resources :pictures, only: [:show, :destroy, :index, :create, :update]
end
app > controllers > pictures_controller.rbclass PicturesController < ApplicationController
# see line 37-38
before_action :set_picture, only: [:show, :destroy, :update]
def index
# this orders all pictures starting with the last created
@pictures = Picture.all.order(created_at: :desc)
render json: @pictures, status: :ok
end
# creates an instance of a picture and returns the status
# "created" if the action is successful
def create
@picture = Picture.create!(picture_params)
render json: @picture, status: :created
end
# allows us add or reduce likes from the front end
def update
@pictures # we need to send the entire set of pictures back.
@picture.update(picture_params)# update a specific pic

end
# deletes a picture
def destroy
@picture.destroy
head :no_content
end
# displays more info about a picture
def show
render json: @picture, status: :ok
end
private
# In rails we need this method to allow us collect/whitelist
# information from the user to store in our DB

def picture_params
params.permit(:img_link, :caption, :created_by, :likes, :liked)
end
# we need to grab the id of the intended picture
# before we can run show more info or delete it.

def set_picture
@picture = Picture.find(params[:id])
end
end
app > controllers > concerns > exception_handler.rbmodule ExceptionHandler
extend ActiveSupport::Concern

included do
rescue_from ActiveRecord::RecordNotFound do |e|
json_response({ message: e.message }, :not_found)
end

rescue_from ActiveRecord::RecordInvalid do |e|
json_response({ message: e.message }, :unprocessable_entity)
end
end
end
app > controllers > concerns > json_response.rb
module Response
def json_response(object, status = :ok)
render json: object, status: status
end
end
class ApplicationController < ActionController::API
include Response
include ExceptionHandler
end
rails s
  1. Select the POST method (create action in our controllers).
  2. Head over to the body tab and select x-www-form-urlencoded. You need to have this checked to make POST requests.
  3. type out the following data(or yours) into the “Key” and “Value” columns.
  4. Hit send.
  5. You should get data back with status:201 Created, just as we defined it in our controllers!
Making a POST request to our rails server with Postman.
I have an ID of 2 because I already created a picture in the DB.
Making a GET request to show a picture with an ID of 2
Making a DELETE request to our controller to delete picture with ID:1
gem ‘rack-cors’, require: ‘rack/cors’
bundle install
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:3000' # the URL for our frontend app
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials:true # adding this for when we decide to build auth
end
end
git branch
git add .
git commit -m "controller actions complete"
git checkout development
git merge controllers
Picture.create!(
img_link: 'https://images.unsplash.com/photo-1517533564579-4e4cef1505c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60',
likes: 2,
created_by: 'Osas',
liked: 'true',
caption: 'Picture by Sharon Garcia from Unsplash'
)Picture.create!(
img_link: 'https://images.unsplash.com/photo-1555029510-40401d84c73c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80',
likes: 0,
created_by: 'Chisom',
liked: 'false',
caption: 'Picture by Olu Famule from Unsplash'
)
> rails db:seed # this command populates our DB
> rails c # opens up the rails console
> Picture.connection # creates a connection to our Picture table
> Picture.all
  1. Head to your heroku dashboard. Create an account if you don’t have one.
  2. Click ‘New’ > ‘Create New App’
  3. Give your app a name. Mine is ‘instabox-api’. Click create app.
  4. Follow the instructions to install the heroku cli and login.
  5. We already have git initialized locally. All we need to do now is reference our heroku repo. heroku git:remote -a instabox-api. Add this in your terminal the root directory of our project.
  6. Then
git push heroku master
#drop the db
rails db:drop
# add pg gem
gem ‘pg’, ‘>= 0.18’, ‘< 2.0’
comment out sqlite3bundle install
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: instabox_api
test:
<<: *default
database: instabox_api_test
production:
<<: *default
database: instabox_api_production
username: instabox_api
password: <%= ENV['INSTABOX_API_DATABASE_PASSWORD'] %>
rails db:create db:migrate db:seed
> while on the master branchgit add .
git commit -m “update db to pgsq”
git push heroku master
heroku run rake db:migrate
heroku run rake db:seed

--

--

--

Growth at Moni(YC W22) | Adebola.dev

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Race condition in Tendermint’s StarPort

The helpful use of partials with locals to make our code reusable and dry.

Task #5 is live on the Task Marketplace

Python stories, September 2018

Debugging a Raspberry Pi 4 Automotive Grade Linux with Visual Studio Code

Python stories, July 2018

How to code ? — 6

Xcode Thread Sanitizer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Adebola Adeniran

Adebola Adeniran

Growth at Moni(YC W22) | Adebola.dev

More from Medium

Incorporating Stripe Payment in React with backend rails

React frontend application using active record to interact with database.

A Rails API for an Image Gallery Based Project with a React Front-End — Part I

Creating a React app with Rails API backend