DEV Community

Cover image for Communication between Sinatra Routes and React Fetch Requests
Julie Evans
Julie Evans

Posted on

Communication between Sinatra Routes and React Fetch Requests

What are Routes and Requests anyways?

For those who don’t know what Sinatra routes or React fetch requests are, I’ll give a brief rundown. Sinatra routes are the communication between you and the database, and function similarly to URLs. React fetch requests are a way for React to communicate with a backend like an API or database, and in this case using the Sinatra routes as a medium or mediator.

What is this all about?

Although this title is pretty long, it sums up all that I plan to say. First I'd like to say that this was an issue I came across, and it took me awhile to figure out what the problem was. I struggled with it because I had assumed my mistake was on my frontend, like a typo or something. I thought this because of how complicated the part I was working on was. It was my EDIT button, and I had the data traveling through several components back and forth, as well as through different functions and nested if statements.

To figure out that it was not my frontend that had the error, I turned off the data being sent so that I was left with just the components. I went through each step that the data would travel through, and checked what it was with a console.log. In doing this I saw that it was not my complicated traveling or nested if statements. The problem lied in the data itself, or to be more accurate what happened to the data as it travel.

While I logged the data at each step, I noticed a discrepancy. The data I sent to be edited and the data sent back after being edited were different. I was a slight difference and a small mistake on my part, so I simply didn't notice it. In fact, I hadn't noticed it until it was breaking my code.

The mistake was in my databases, or more specifically my routes. I had my Sinatra routes looking like this:

patch "/items/:id" do
    item = Item.find(params[:id])
    item.update(
        name: params[:name],
        price: params[:price],
        priority: params[:priority],
        category: params[:category],
        balance_id: params[:balance_id]
    )
    item.to_json
end
Enter fullscreen mode Exit fullscreen mode

With the route like this, the data received afterwards would look like this:

{
    name: placeholder name,
    price: 0.00,
    priority: 1,
    category: placeholder category,
    balance_id: 1
}
Enter fullscreen mode Exit fullscreen mode

The problem was that my data being sent to it looked like this:

{
    name: placeholder name,
    price: 0.00,
    priority: 1,
    category: placeholder category,
    balance: {
        id: 1,
        name: placeholder name,
        amount: 0.00
    }
}
Enter fullscreen mode Exit fullscreen mode

So what was happening was that the data I sent to be edited had a "balance" key, whereas the edited data I got back had a "balance_id". My code was breaking because when I tried to render the new data it would try to read the keys of the object under the "balance" key, but it no longer existed.

It all had to do with what my Sinatra routes gave and received, but my databases and how my data was connected between them also played a part. To explain I'll first explain what my databases and data was doing on the backend. To start the data in my databases and how they were connected could be best shown with this diagram:

diagram

I had a one-to-many relationship between my balances and items databases, with a foreign id connecting them. This was all fine and good up until I was writing my routes. Having my routes have the "balance_id" key was not the problem, since that was the connecting factor. Because I was working with items I needed the keys to match the columns on the database, which I did. The problem was that my other routes had the data shown differently that how it was received. I needed the data received to be consistent throughout all of my routes.

The fix was simple, since how I was sending my data was not the issue. I simply needed to add the same statement specifying how I want the data to look, or in other words what the data received was. Which was the "only" and "includes" I had added to the other routes, but had forgotten to add here. The fixed version of my Sinatra route looked liked this:

patch "/items/:id" do
    item = Item.find(params[:id])
    item.update(
        name: params[:name],
        price: params[:price],
        priority: params[:priority],
        category: params[:category],
        balance_id: params[:balance_id]
    )
    item.to_json(only: [:id, :name, :price, :priority, :category], include: :balance)
end
Enter fullscreen mode Exit fullscreen mode

Although the data in essence was the same, how it looked and communicated between the frontend and backend was different. I found this subtle difference fascinating, and could apparently make-or-break my code. What was even more interesting was that my fetch request in React looked the same as the database, the same as the routes. It looked like this:

const formData = {
    name: itemName,
    price: itemPrice,
    priority: itemPriority,
    category: itemCategory,
    balance_id: balanceId,
}

fetch(`http://localhost:9292/items/${id}`, {
    method: "PATCH",
    headers: {
        "Content-Type": "application/json"
    },
    body: JSON.stringify(formData)
})
    .then(res => res.json())
    .then(data => onEditSubmit(data))
Enter fullscreen mode Exit fullscreen mode

The "formData" here in the fetch request and the part inside of the update in the route are exactly the same. They need to be identical in order to work with database, where they need to match the columns. These parts need to be identical to function. But since I had the balances database attached to the items and an add on, neither the data nor the databases changed, simply the way it looked afterwards.

Both the fetch requests and routes communicate with each other and the databases the same way. I simply told it to show me the balance connect to the item instead of the foreign id, which was all in the only and includes attached to the "to_json" on the routes. The "only part specified what keys to display. And the "includes" told it to include the whole balance (instance) connected, since I could do that with how my databases were set up. The fetch request on the other end communicates with the databases with the routes as mediators, so without specifying what to display with these "only" and "includes" statements the data would look identical to the default or simply the database itself.

So the takeaway is that the data sent needs to be identical regardless if it is in the route, fetch request, or the database. The only thing is that if you specify how the data received looks, you must be consistent.

Latest comments (0)