DEV Community 👩‍💻👨‍💻

marskimiko
marskimiko

Posted on • Updated on

How to make a full-stack CRUD app with React and a Ruby Sinatra API

In this tutorial I will be guiding you through the process of building a full-stack CRUD application with React and a Ruby Sinatra API.

What is Sinatra?

Sinatra is a free open source library and domain-specific language written in Ruby, is a good alternative to Ruby on Rails, and is dependent on the Rack web server interface. Sinatra is considered a 'microframework' and focuses on "quickly creating web applications in Ruby with minimal effort" -sinatrarb.com

What is React?

React is a free open-source front-end JavaScript library built by facebook for building user interfaces based on UI components. It can be used as a base in the development of single-page applications.

What is CRUD?

CREATE, READ UPDATE, and DELETE are the database commands that make up the foundations of CRUD

Routes used in my application:

Name Method Route Purpose
Create POST /model Creates a new record
Read GET /model Reads & retrieves data based on input parameters
Update PUT/PATCH /model/:id Updates data without overwriting it
Destroy DELETE /model/:id Removes data from the database

CRUD will be used in this application as a means to retrieve data, create new data, and allow the user to interact with it by editing and deleting the data. I will be using my project from Flatiron School as an example, which is a Job Board application for quick gigs called "Quicky".

Back-End

First a file tour!

The config.ru file is set up so that when the file is run it will require the Environment file, which will make sure there is a connection to the database and then run the ApplicationController which is a class that inherits from the Sinatra gem (base module from within that gem), which contains some starter methods in the controller for you.

Gemfile:

Gemfile

config.ru:

Image description

environment.rb:

Image description

Rakefile:

Image description

Corneal-new

To start my backend I used the Corneal-new: gem that creates a Sinatra skeleton for you to easily build off of when coding your application.

Install the gem, run 'corneal new APP-NAME', and run 'bundle install'. You can start up the server by typing 'shotgun' into the terminal and verify everything is working.

In the terminal run 'corneal scaffold modelName' to create a model and migration for you to create the table for the model, and controller for the model as well.

Database Migrations

Once your specify the attributes you want in your model table run "rake db:migrate" in the terminal in order to run your migrations. Doing this creates a schema file which is a list of logical structures of your data. If you make a mistake and need to edit your migration run "rake db:rollback" then make your edit and run "rake db:migrate" again to run the migration again (simply editing after the fact will not change the schema).

migration files:

Image description

Image description

Active Record Associations

Once you create your migration files build out your models in order to establish you Active Record associations, for my application I had a one-to-many relationship meaning a Category has many Listings and a listing belongs to a category (has one category):

class Cat < ActiveRecord::Base
  has_many :listings
end
Enter fullscreen mode Exit fullscreen mode
class Listing < ActiveRecord::Base
  belongs_to :cat
end
Enter fullscreen mode Exit fullscreen mode

Controllers

From here you can start with your CRUD in your controllers. A good place to start would be with a "Read" route which should return all categories when a get request is sent to '/cats'.

Read (GET)

#RUBY BACKEND
#Request type is specified before URL endpoint.
#URL endpoint points to all categories
get "/cats" do
 # return all categories posts after request
 #Requests turned to JSON, for REACT readability.
   cats = Cat.all
   cats.to_json
end
Enter fullscreen mode Exit fullscreen mode

Read (GET)

  • to view go to http://127.0.0.1:9393/:id
  • this route allows you to see an individual category based on the id you provide in the url
get '/cats/:id' do
  cat = Cat.find_by_id(params[:id])
  cat.to_json
end

Enter fullscreen mode Exit fullscreen mode

Creat(POST)

  • This route typically receives information in the request body and creates a new data entry which the user can do from the browser by interacting with the front-end
post "/cats" do
   cat = Cat.create(
     job_type: params[:job_type]
   )
   cat.to_json  
end
Enter fullscreen mode Exit fullscreen mode

Update (PATCH)

  • This route receives the id of the category which is being updated in the url and is then updated using the data passed in via the request body
patch '/dats/:id' do
 cat = Cat.find(params[:id])
 cat.update(
  job_type: params[:job_type]
 )
 cat.to_json)
end 
Enter fullscreen mode Exit fullscreen mode

DELETE

  • This route takes an id of a category to be deleted in the url and then deletes it using the .destroy method
delete '/cats/:id' do
   category = Cat.find(params[:id])
   category.destroy
end
Enter fullscreen mode Exit fullscreen mode

These are basic examples of CRUD routes and functionality you can implement into your applications. Here are both of my controllers to give you an idea of other options you can build into your application

Image description

Image description

Front-End

For this section of the project I will explain how to build out your front-end to connect with your back-end. This tutorial is assuming you know React but simply need help connecting your front-end to your back-end.

Run 'npx create-react-app my-app' in the terminal to create a quick skeleton of all the file structures you need to begin building out your front end. Build your components out as you would like for what makes sense for your specific project.

Read(GET)

useEffect(() => {
    fetch("http://localhost:9393/cats")
    .then((r) => r.json())
    .then((data) => {
      setCats(data);
    });
  }, []);
Enter fullscreen mode Exit fullscreen mode

Create(POST)

const configObj = {
   method: "POST",
   headers: {
     Accept: "application/json",
     "Content-Type": "application/json",
   },
    body: JSON.stringify({
     job_type: jobType
    }),
};

const handleSubmit = (e) => {
    e.preventDefault();

    fetch("http://localhost:9393/cats", configObj)
      .then((r) => r.json())
      .then((cat) => {
        addNewCategory(cat);
      });
  };
Enter fullscreen mode Exit fullscreen mode

*Update(PATCH) *

 function handleEditCategory(e) {
    e.preventDefault();

    fetch(`http://localhost:9393/cats/${id}`, {
    method: "PATCH",
    headers: { 
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(formData),
   })
    .then ((response) => response.json())
    .then((updatedCategory) => {
      handleUpdateCategory(updatedCategory);
    });
  }
Enter fullscreen mode Exit fullscreen mode

DELETE

const handleDelete = () => {
    fetch(`http://localhost:9393/cats/${id}`, {
      method: 'DELETE',
    });
    const updatedCats = cats.filter((cat) => cat.id !== id);
    setCats(updatedCats);
  };
Enter fullscreen mode Exit fullscreen mode

Hopefully this tutorial has been helpful in guiding you on how to start building full-stack applications. Here's a link to the front-end and back-end GitHub repositories for my application.

Top comments (0)

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.