How to make a full-stack CRUD app with React and a Ruby Sinatra API
Tags: ruby, react, sinatra, api
Published: 2022-11-01 18:45:47

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. 


<u>Gemfile:</u>

![Gemfile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jn1tjxeni3jit4rytigd.png)

<u>config.ru:</u>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flt4z2xovn2d6p236760.png)

<u>environment.rb:</u>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mma8p0ssqq9dizfj8zd.png)

<u>Rakefile:</u>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhzhnkaay3n89czo2tah.png)

**Corneal-new**

To start my backend I used the [Corneal-new:](https://rubygems.org/gems/corneal-new) gem that creates a Sinatra skeleton for you to easily build off of when coding your application. 

Install the [gem](https://github.com/thebrianemory/corneal), 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52flnfa93m01mb8pvi14.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofa18b10dof66x25gnf4.png)

**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
```

```
class Listing < ActiveRecord::Base
  belongs_to :cat
end
```
**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)**

- to view go to http://127.0.0.1:9393/cats in your browser(when you run shotgun it will provide a url for you in the terminal)
```
#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
```

**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

```

**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
```

**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 
```

**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
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hip0d9os4o5wv09h8ryl.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uf9u1ykgmocvp4p3otz1.png)

**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);
    });
  }, []);
```

**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);
      });
  };
```

**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);
    });
  }
```

**DELETE**

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

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](https://github.com/marskimiko/job-board-front-end) and [back-end](https://github.com/marskimiko/job-board-app) GitHub repositories for my application.