Rails Restful Routing
Tags: rails, webdev, programming, tutorial
Published: 2022-11-17 10:59:41

When building an interactive full-stack website, there are going to be cases where the user communicates with the server, and the server has to respond with or update the corresponding data. In the simplest of applications, it's the reason why a user can create, read, update, and destroy (CRUD) particular pieces of data. 


![The MVC Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09i4te6u9sjtey0wf2vi.png)

This is one of the many fundamental properties that Rails simplifies for us in web applications. The **User** sends a request to the **Controller**, which manipulates the request to be interpretable by the **Model** so it can update the **View** and present it back to the **User**.

## What is REST?
REST is REpresentational State Transfer which references an architectural design flow/pattern when routing your HTTP requests. Often times, requests like `GET`, `POST`, etc are used more than once for a certain controller and the definitions for each varying `GET` or `POST` request will look and execute messily. To avoid this, the rails backend structures 7 specific route names that translate the HTTP request smoothly once defined in the specific controller.

For example, a lot of `GET` requests without this convention are written in Sinatra with
```
class ApplicationController < Sinatra::Base

  get '/games' do
    games = Game.all
    games.to_json data
  end

end
```
As you can see, to satisfy a simple `SELECT *` statement, a get request is used and must used over and over again to specific different types of `get` requests.

![7 Common Rails Routes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jbmgoqlirma6gsgc8593.png)

## Route Setup
In your `routes.db` in your Ruby application, this is where the specifications of the different routes per controller are defined.
```
Rails.application.routes.draw do
   resources :games
end
```
Having no particular declarations after the `:games` attribute has rails interpret all 7 routes to be implemented. If you only wanted to have a `CREATE` route, then you would add
```
Rails.application.routes.draw do
   resources :games, only: [:create]
end
```

## Index | Show
Index and Show in a RESTful context both reference the `GET` HTTP pull method. `Index` would list the entire index of a certain table, and `Show` would represent only one row of that table

In `routes.db`, further clarification is needed
```
Rails.application.routes.draw do
   resources :games, only: [:index, :show]
end
```

In the controller for the respective class (in this case, a `game_controller.rb` file), the implementation is
```
class GameController < ApplicationController
   def index
      render json: Games.all
   end
end
```
Here, we tell the database to get all indices of a table and render them back to the Model
You can also tack on a status code if need be
```render json: Games.all, status: :ok```

`Show` represents the `GET` HTTP method as well, but with a parameter (typically an ID of the index you'd like)
```
def show
   render json: Games.find(params[:id]), status: :ok
end
```
If you need to cover for the situation in which the ID wasn't found then you can nest it in an if-else scenario
```
def show
   game = Games.find(params[:id])
   if game
      render json: game, status: :ok
   else
      render json: {error: "Game not found"}, status: :not_found
   end
end
```

## Create | Update | Destroy
Once again we specify our `routes.db` to include these HTTP requests
```
Rails.application.routes.draw do
   resources :games, only: [:index, :show, :create, :update, :delete]
end
```
When creating entries, rails can not automatically assume the contents of a `POST` request, we need to re-orient the parameters so that the body of the post request can be interpreted to create new entries
Adding this `wrap_parameters` declaration tells rails to re-format the data into a `[]` array-like format, so that we can break apart what was sent and see if there are extra, or lacking pieces of data
```
class GameController < ApplicationController
wrap_parameters format: []
```

The `CREATE` request is defined as follows

```
def create
   game = Games.create(game_params)
   render json: game, status: :created
end

private
def game_params
   params.permit(:name, :publisher, :release_year, :console)
end
```
The game_params function takes the params and assigns each attribute to be returned as parameters, so it is properly formatted when passed into the `create` function

`Update` would replicate the `show` route but with a small change when rendering
```
def update
   game = Games.find(params[:id])
   if game
      game.update(game_params)
      render json: game, status: :accepted
   else
      render json: {error: "Game not found"}, status: :not_found
   end
end
```
`Destroy` would be similar to above as well, the only niche is that since there isn't necessarily any data to render back to the Model, you would return `head :no_content`
```
def destroy
   game = Games.find(params[:id])
   if game
      game.destroy
      head :no_content
   else
      render json: {error: "Game not found"}, status: :not_found
   end
end
```

## Conclusion
Overall, as fundamentally standard CRUD is across all forms of database management/administration, the only extra step needed when adapting to new environments is following the conventions. `New` and `Edit` would require a form to be presented and rendered, in which case the `Create` and `Update` definitions would be invoked afterwards.
Happy CRUDing!

## References
MVC: Model, View, Controller (https://www.codecademy.com/article/mvc)

ThoughtBot: REST Tutorial (https://thoughtbot.com/upcase/videos/rest)

RailsGuides: Rails Routing (https://guides.rubyonrails.org/routing.html)

MakandraCards: wrap_parameters for your API
(https://makandracards.com/makandra/46405-rails-wrap_parameters-for-your-api)