DEV Community

Elias Ventura
Elias Ventura

Posted on

Fetching and nested routing and user_id!

While working on my fourth project for Flatiron School I really was able to see how web applications map the ActiveRecord relationships in the models, with the way you send fetch request to the routes. I was required to make fetch request to get data from the backend server, or delete or patch requests . It seemed pretty straight forward, but i noticed that the id of the main user model was used over and over for different uses. By main user model I mean the model that has_many of another table and whose id is the foreign key of that other table. But also referring to the user table that is used for the signup or login functionality. The id attribute of that table seems to be one of the more important attributes at least in the way my project is setup. I have user class that has a one-to-many relationship with a predictions class.

Firstly the id of user id is used to even initially establish the relation ship. ActiveRecord need the user id to incorporate in the table that belongs_to to the user by being that tables foreign key. Without this foreign key the associated belongs_to table wouldn't even be able to be created, especially because the in the case of my project, that table is used as a join table. Without the user id the other table that is associated to the join table would not be able to access the user through the has_many, through: relationship.

So this code right here the has_many :users, through: predictions line would break and send an error:

class Game < ApplicationRecord
   has_many :predictions
   has_many :users, through: :predictions
end
Enter fullscreen mode Exit fullscreen mode

Firstly the user model is used for signing and logging in. When the login form is submitted, it send a request to the route, then the create method in the sessions controller will handle this request. It will then set the cookie by setting the session[:user_id] equal to the user.id.

def create
        user = User.find_by(username: params[:username])

        if user&.authenticate(params[:password])
        session[:user_id] = user.id
        render json: user, status: :created
        else
            render json: { errors: {login: "Invalid username or password"} }, status: :unauthorized
        end
    end
Enter fullscreen mode Exit fullscreen mode

This in turn is used by the entire app authentication to make sure the user is logged in and that it is the correct user that is trying to execute the action. Specifically because there is a useEffect() in the top level in the App.js file that gets loaded before all the other components. In the useEffect there is a fetch request to identify who is the current user before any of the rest of the frontend is populated.

useEffect(() => {
    fetch("/me").then((response) => {
      if (response.ok) {
        response.json().then((user) => {
          setCurrentUser(user)

        });
      }
    });
  }, [])
Enter fullscreen mode Exit fullscreen mode

The sessions#destroy action also user the user.id to clear the cookie and log the user out.

Secondly, the user id is used for the apps resource because they are also set up to have nested routing. So basically the predictions resources are nested within the users resources. This allows access to the predictions but only through the user that they are related to , which resembles the active record associations where predictions belongs_to user. All the predictions in the Predictions table are still accessible but it doesn't make sense for a user to be able to access or edit another users predictions.
So when the associations and routes are set up this way , and we want the users to see only their data and to only be able to manipulate their data the user id comes into play again.

This code will get all your predictions, but only those that are related to the current user:

fetch(`/users/${current_user.id}/predictions`)
                .then( r => r.json())
                .then(data => {
                    console.log(current_user.id)
                    console.log(data)
                    setPredictions(data)}
                    )
Enter fullscreen mode Exit fullscreen mode

This code will send a delete request to the predictions table, but only to the predictions that are related to the user once again:

 function handleDelete(event, prediction) {
        fetch(`users/${current_user.id}/predictions/${prediction.id}`, {
            method: 'DELETE',
        })
        .then( r => r.json())
        .then( data => deletePrediction(prediction)) 

    }
Enter fullscreen mode Exit fullscreen mode

and so in this way we can protect the individual users information. So we see how the users id attribute is used once again.

Finally the user id, through the cookie is also used in the error rendering and authorization. In the application controller we have some error handling that gets inherited by all the controllers where before any actions run the controller checks to see if the cookie is set to the user id.

So if theres any problem with the user id during the creation of the user, or any changes that affect that relationship, there will be errors caused, no resources loaded and this also helps to prevent other users from tampering with each others logged in information.

Top comments (0)