💧🍔 Projeto Rockelivery: API para Pedidos em um Restaurante com Elixir e Phoenix (Parte 3)
Tags: elixir, phoenix, ecto, braziliandevs
Published: 2021-06-11 19:51:30

Essa é a terceira parte do projeto __Rockelivery__. Esse projeto faz parte do Bootcamp da [Rocketseat](https://rocketseat.com.br/), ministrado pelo professor [Rafael Camarda](https://www.instagram.com/rafacamarda/).

## Conteúdo:
[📇 Módulo para Leitura de Usuários](#modulo-para-leitura-de-usuarios)
[🔎 A Rota de Show dos Usuários](#a-rota-de-show-dos-usuarios)
[💀 A Rota de Deleção de Usuários](#a-rota-de-delecao-de-usuarios)
[🔴 Centralizando as Mensagens de Erro](#centralizando-as-mensagens-de-erro)
[📝 Módulo para Update de Usuários](#modulo-para-update-de-usuarios)
[🔄 A Rota Update](#a-rota-update)
[🔌 Criando um Plug](#criando-um-plug)

[![A Jornada do Autodidata em Inglês](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkoqkmh9hic17tnddjf6.png)](https://go.hotmart.com/R93865620U)

## 📇 Módulo para Leitura de Usuários <a name="modulo-para-leitura-de-usuarios"></a>

Vamos agora fazer um módulo para pesquisar os usuários no banco de dados.

Lembrando que podemos pegar todos os usuários do banco de dados usando a função `Rockelivery.Repo.all()`:

```elixir
iex> Rockelivery.Repo.all(Rockelivery.User)
[debug] QUERY OK source="users" db=22.5ms decode=3.6ms queue=3.2ms idle=705.1ms
SELECT u0."id", u0."address", u0."age", u0."cep", u0."cpf", u0."email", u0."name", u0."password_hash", u0."inserted_at", u0."updated_at" FROM "users" AS u0 []
[
  %Rockelivery.User{
    __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
    address: "Rua...",
    age: 28,
    cep: "12345678",
    cpf: "12345678910",
    email: "maiqui@teste.com",
    id: "482f95a7-b447-42e9-ae67-aef72954c3f0",
    inserted_at: ~N[2021-06-05 14:45:28],
    name: "Maiqui",
    password: nil,
    password_hash: "$argon2id$v=19$m=131072,t=8,p=4$L3mKYnhn5ooDZRsyd7maaA$92xStD9oCV06HtJYnoYhGY4HBJK69bUvb6HpTv+IlPc",
    updated_at: ~N[2021-06-05 14:45:28]
  },
...
```

Ou buscar um usuário específico passando o ID desse usuário para a função `Rockelivery.Repo.get()`. Note que quando o usuário é encontrado, um `Schema` é retornado: 

```elixir
iex> Rockelivery.Repo.get(Rockelivery.User, "482f95a7-b447-42e9-ae67-aef72954c3f0")
[debug] QUERY OK source="users" db=2.9ms queue=2.4ms idle=1418.4ms
SELECT u0."id", u0."address", u0."age", u0."cep", u0."cpf", u0."email", u0."name", u0."password_hash", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [<<72, 47, 149, 167, 180, 71, 66, 233, 174, 103, 174, 247, 41, 84, 195, 240>>]
%Rockelivery.User{
  __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
  address: "Rua...",
  age: 28,
  cep: "12345678",
  cpf: "12345678910",
  email: "maiqui@teste.com",
  id: "482f95a7-b447-42e9-ae67-aef72954c3f0",
  inserted_at: ~N[2021-06-05 14:45:28],
  name: "Maiqui",
  password: nil,
  password_hash: "$argon2id$v=19$m=131072,t=8,p=4$L3mKYnhn5ooDZRsyd7maaA$92xStD9oCV06HtJYnoYhGY4HBJK69bUvb6HpTv+IlPc",
  updated_at: ~N[2021-06-05 14:45:28]
}
```

Mas quando o usuário não é encontrado, um `nil` é retornado:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3fwi8szzuw250itsh26.png)

Crie o arquivo `lib/rockelivery/users/get.ex`:

```elixir
defmodule Rockelivery.Users.Get do
  alias Rockelivery.{Repo, User}

  def by_id(id) do
    case Repo.get(User, id) do
      nil -> {:error, %{status: :not_found, result: "User not found"}}
      user_schema -> {:ok, user_schema}
    end
  end
end
```

Testando com um ID inválido:
```elixir
iex> Rockelivery.Users.Get.by_id("482f95a7-b447-42e9-ae67-aef72954c3f1")
[debug] QUERY OK source="users" db=4.6ms queue=0.1ms idle=1023.1ms
SELECT u0."id", u0."address", u0."age", u0."cep", u0."cpf", u0."email", u0."name", u0."password_hash", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [<<72, 47, 149, 167, 180, 71, 66, 233, 174, 103, 174, 247, 41, 84, 195, 241>>]
{:error, %{result: "User not found", status: :not_found}}
```
Testando com um ID válido:
```elixir
iex> Rockelivery.Users.Get.by_id("482f95a7-b447-42e9-ae67-aef72954c3f0")
[debug] QUERY OK source="users" db=2.0ms queue=0.1ms idle=1622.8ms
SELECT u0."id", u0."address", u0."age", u0."cep", u0."cpf", u0."email", u0."name", u0."password_hash", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [<<72, 47, 149, 167, 180, 71, 66, 233, 174, 103, 174, 247, 41, 84, 195, 240>>]
{:ok,
 %Rockelivery.User{
   __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   address: "Rua...",
   age: 28,
   cep: "12345678",
   cpf: "12345678910",
   email: "maiqui@teste.com",
   id: "482f95a7-b447-42e9-ae67-aef72954c3f0",
   inserted_at: ~N[2021-06-05 14:45:28],
   name: "Maiqui",
   password: nil,
   password_hash: "$argon2id$v=19$m=131072,t=8,p=4$L3mKYnhn5ooDZRsyd7maaA$92xStD9oCV06HtJYnoYhGY4HBJK69bUvb6HpTv+IlPc",
   updated_at: ~N[2021-06-05 14:45:28]
 }}
```
Se tentarmos usar um ID que não seja no formato UUID, receberemos uma exceção:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yq1uykb5pjwmpxuu4gw8.png)

Então precisamos refatorar o nosso código para verificar se um ID é um UUID. Vamos antes só entender a função `Ecto.UUID.cast/1`:

```elixir
iex> Ecto.UUID.cast("123456")
:error

iex> Ecto.UUID.cast("482f95a7-b447-42e9-ae67-aef72954c3f0")
{:ok, "482f95a7-b447-42e9-ae67-aef72954c3f0"}
``` 
Em `lib/rockelivery/users/get.ex`:

```elixir
defmodule Rockelivery.Users.Get do
  alias Ecto.UUID
  alias Rockelivery.{Repo, User}

  def by_id(id) do
    case UUID.cast(id) do
      :error -> {:error, %{status: :bad_request, result: "Invalid Format!"}}
      {:ok, uuid} -> get(uuid)
    end
  end

  defp get(id) do
    case Repo.get(User, id) do
      nil -> {:error, %{status: :not_found, result: "User not found!"}}
      user_schema -> {:ok, user_schema}
    end
  end
end
```

## 🔎 A Rota de Show dos Usuários <a name="a-rota-de-show-dos-usuarios"></a>

A partir de agora vamos construir todo o código para que a rota show funcione. Lembrando que a rota já foi criada quando usamos o código `resources`:

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

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

#### Fachada para a função by_id

Vamos construir a nossa fachada para a função `Rockelivery.Users.Get.by_id/1`, já que precisaremos dela na action `show` do controller `RockeliveryWeb.UsersController`.

Em `lib/rockelivery.ex`:

```elixir
defmodule Rockelivery do
  alias Rockelivery.Users.Create, as: UserCreate

  # adicione
  alias Rockelivery.Users.Get, as: UserGet

  defdelegate create_user(params), to: UserCreate, as: :call
  
  # adicione 
  defdelegate get_user_by_id(id), to: UserGet, as: :by_id
end
```

#### Construindo a action show

Em `lib/rockelivery_web/controllers/user_controller.ex` adicione o código abaixo:

```elixir
def show(conn, %{"id" => id}) do
  with {:ok, %User{} = user} <- Rockelivery.get_user_by_id(id) do
    conn
    |> put_status(:ok)
    |> render("show.json", user: user)
  end
end
```

Ao contrário da action `index`, invés de `params` estamos fazendo __Pattern Matching__ colocando `%{"id" => id}`.

#### Adicionando "show.json" na view

Em `lib/rockelivery_web/views/users_view.ex` adicione o código abaixo:

```elixir
def render("show.json", %{user: %User{} = user}), do: %{user: user}
```

#### Testando a nossa rota show

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8idelkb32usq6918dj2q.png)![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgunmsno7ae0rwlyxfa6.png)

Vamos passar um `id` com formato inválido para ver a mensagem:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l98idj82nrvx3aycbb2c.png) 

#### Ajustando a mensagem

Em `lib/rockelivery_web/views/error_view.ex` adicione o código abaixo:

```elixir
def render("error.json", %{result: %Changeset{} = changeset}) do
  %{message: translate_errors(changeset)}
end
# abaixo do código acima
# adicione
def render("error.json", %{result: error_message}) do
  %{message: error_message}
end
``` 

Formato do ID inválido:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fafl12g37kwm96oc00h8.png) 

Formato válido, mas o ID não existe no banco:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqcxncsq80zd72br5v88.png)

#### Refatoração

Lembrando que antes do erro ir para a `error_view`, ele passa no `FallbackController` e nosso `result` que recebia só um `changeset` agora recebe também uma mensagem, então vamos alterar o nosso código. Podemos alterar o nome da variável `changeset` para `result`, ou para `changeset_or_message` que fica mais claro ao ser lido:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/detdtvs3rcgm53c72kuu.png)
 
## 💀 A Rota de Deleção de Usuários <a name="a-rota-de-delecao-de-usuarios"></a>

Vamos começar com o módulo `Delete`. Ele vai ser parecido com o módulo `Get`.

#### Módulo Delete

Crie um arquivo em `lib/rockelivery/users/delete.ex`, e o conteúdo deve ter essa aparência:

```elixir
defmodule Rockelivery.Users.Delete do
  alias Ecto.UUID
  alias Rockelivery.{Repo, User}

  def call(id) do
    case UUID.cast(id) do
      :error -> {:error, %{status: :bad_request, result: "Invalid Format!"}}
      {:ok, uuid} -> delete(uuid)
    end
  end

  defp delete(id) do
    case Repo.get(User, id) do
      nil -> {:error, %{status: :not_found, result: "User not found!"}}
      user_schema -> Repo.delete(user_schema)
    end
  end
end
```

Vamos agora fazer a fachada para este módulo.

#### Delete Facade

Em `lib/rockelivery.ex` adicione o código abaixo:

```elixir
alias Rockelivery.Users.Delete, as: UserDelete

defdelegate delete_user(id), to: UserDelete, as: :call
```

#### Delete Action

Em `lib/rockelivery_web/controllers/users_controller.ex` adicione o código abaixo:

```elixir
def delete(conn, %{"id" => id}) do
    with {:ok, %User{}} <- Rockelivery.delete_user(id) do
      conn
      |> put_status(:no_content)
      |> text("")
    end
end
```

Como estamos colocando um status `:no_content`, não vamos devolver nenhum `body`, por isso não vamos usar a função render, apenas devolveremos uma string vazia.

#### Testando a deleção

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dfhgzk3w7bkvb1l7m4a.png)

## 🔴 Centralizando as Mensagens de Erro <a name="centralizando-as-mensagens-de-erro"></a>

Agora existe uma oportunidade para refarorarmos o nosso código. Podemos observar que começamos a repetir as mensagens de erro e os status code:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yyyki5ujx2alrwpw35np.png)
 
Vamos então centralizar essas menssagens em um arquivo só.

#### Criando um arquivo para as mensagens de erro

Crie `lib/rockelivery/error.ex` e o seu conteúdo deve ter essa aparência:

```elixir
defmodule Rockelivery.Error do
  @keys [:status, :result]

  @enforce_keys @keys

  defstruct @keys

  def build(status, result) do
    %__MODULE__{
      status: status,
      result: result
    }
  end

  def build_user_not_found, do: build(:not_found, "User not found")
  def build_invalid_id_format, do: build(:bad_request, "Invalid id format")
end
```

#### Refatorando

Em `lib/rockelivery/users/get.ex`:

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

__Faça o mesmo em__ `lib/rockelivery/users/delete.ex`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/krsb2t9cc9jk2cj2zzg3.png) 

E, em `lib/rockelivery/users/create.ex` use apenas a função `Error.build/2`:

```elixir
defp handle_insert({:error, changeset}) do
    # ANTES
    # {:error, %{status: :bad_request, result: changeset}}

    # DEPOIS
    {:error, Error.build(:bad_request, changeset)}
end
```

Vamos alterar também o `FallbackController` e colocar agora a nossa `struct error`. Com ela nosso código fica mais declarativo, ficando mais fácil entender o código. 

Em `lib/rockelivery_web/controllers/fallback_controller.ex`:

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

## 📝 Módulo para Update de Usuários <a name="modulo-para-update-de-usuarios"></a>

Primeiramente vamos verificar na documentação como funciona o callback `Ecto.Repo.update`: https://hexdocs.pm/ecto/Ecto.Repo-callback-update.html

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1e2qych358sog9w6zw3v.png) 

No exemplo da documentação podemos ver que precisaremos de um `changeset`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dmz0psxkx3mg2a4po7f1.png) 

Vamos criar o arquivo `lib/rockelivery/users/update.ex`:

```elixir
defmodule Rockelivery.Users.Update do
  alias Ecto.UUID
  alias Rockelivery.{Error, Repo, User}

  def call(%{"id" => id} = params) do
    case UUID.cast(id) do
      :error -> {:error, Error.build_invalid_id_format()}
      {:ok, _uuid} -> update(params)
    end
  end

  defp update(%{"id" => id} = params) do
    case Repo.get(User, id) do
      nil -> {:error, Error.build_user_not_found()}
      user_schema -> do_update(user_schema, params)
    end
  end

  defp do_update(%User{} = user, %{} = params) do
    user
    |> User.changeset(params)
    |> Repo.update()
  end
end
```

Precisamos alterar também `lib/rockelivery/user.ex` para podermos passar um `schema` como parâmetro:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2iflj0gdfigog2foqof1.png)

#### Testando o Update

Buscando  todos os usuários cadastrados com o comando:

```elixir
iex> Rockelivery.Repo.all Rockelivery.User
``` 
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2ruz0wcpqzruacenp0n.png) 

Pegamos um ID e vamos tentar atualizar o nome:

* Criando os parâmetros:
```elixir
iex> params = %{"id" => "482f95a7-b447-42e9-ae67-aef72954c3f0", "name" => "Maiqui Tomé"}
```
* Executando a função Update:
```elixir
iex> Rockelivery.Users.Update.call params
```
* Resultado:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z1a1h40nk8sy6n3z4k2x.png) 
 
Por que não funcionou?
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fnmbiqnvw9a2phdyfias.png)  

Quando passamos o `schema %User{}` com os dados puxados do banco para a função `User.changeset/2` estamos passando com o campo password nulo, pois o banco de dados não retorna os dados dele já que esse campo é virtual. A função `User.changeset/2` faz a validação e diz que esse campo é obrigatório informar. Precisamos refatorar novamente o nosso código.

Em `lib/rockelivery/user.ex`:

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

Em `lib/rockelivery/users/update.ex`:

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

Resultado:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6eyzef8jpnsyv3lcm3c5.png)  

## 🔄 A Rota Update <a name="a-rota-update"></a>

Após o módulo de update criado, vamos partir para a criação de todo o código para a rota update funcionar.

#### Update Facade

Em `lib/rockelivery.ex` adicione o código abaixo:

```elixir
alias Rockelivery.Users.Update, as: UserUpdate

defdelegate update_user(params), to: UserUpdate, as: :call
```

#### Update Action

Em `lib/rockelivery_web/controllers/user_controller.ex` adicione o código abaixo:

```elixir
def show(conn, %{"id" => id}) do
  with {:ok, %User{} = user} <- Rockelivery.get_user_by_id(id) do
      conn
      |> put_status(:ok)
      # alterando "show.json" para "user.json"
      |> render("user.json", user: user)
  end
end
  
# adicione
def update(conn, %{} = params) do
  with {:ok, %User{} = user} <- Rockelivery.update_user(params) do
      conn
      |> put_status(:ok)
      |> render("user.json", user: user)
  end
end
```
Como a action `show` é praticamente igual a action `update` vamos usar o template `"user.json"` para os dois. Vamos alterar agora o código da view.

Em `lib/rockelivery_web/views/users_view.ex` altere `"show.json"` para `"user.json"`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/go1g43zybho9dgcmkrm8.png)
 
#### Testando a Rota Update

Primeiramente, vamos pesquisar um usuário usando a action `show`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19tzztzig28hz7ihq7xp.png)
Para todos os campos aparecerem certifique-se que você acrescentou eles no `Jason.Encoder`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfqyvfcwx6s1h7bx2fk9.png)

Agora vamos editar esse usuário usando a rota nova de update. Vou apenas trocar o nome de __"Maiqui Tomé"__ para __"Maiqui Pirolli Tomé"__:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vt07cpucjrc7856jlinz.png)
    
Ao pesquisar novamente usando a rota Get:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wsr755mfy8xconp3t9te.png)

## 🔌 Criando um Plug <a name="criando-um-plug"></a>

Primeiramente, antes de entendermos melhor o que é um __Plug__, vamos entender porque vamos precisar de um. Podemos perceber na imagem abaixo, que em vários arquivos estamos repetindo o mesmo trecho de código para verificar se um ID é um UUID.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7o2qzgse4gbifvblzkkd.png)

Para melhorar o nosso código precisamos construir um __Plug__. Vamos antes enteder ele melhor. 

__Plug__ é uma convenção para manipular a struct de conexão `%Plug.Conn{}`. Mesmo não fazendo parte do núcleo de Elixir, __Plug__ é um projeto oficial de Elixir. Você pode conferir a documentação oficial do __Plug__ aqui: https://hexdocs.pm/plug/readme.html

A definição da documentação do Phoenix diz que o __Plug__ é uma especificação para módulos combináveis ​​entre aplicativos da web. É também uma camada de abstração para adaptadores de conexão de diferentes servidores da web. A ideia básica do __Plug__ é unificar o conceito de uma "conexão" na qual operamos. Isso difere de outras camadas de middleware HTTP, como Rack do Ruby on Rails, onde a solicitação e a resposta são separadas na pilha de middleware. Essa definição e mais detalhes você encontra aqui: https://hexdocs.pm/phoenix/plug.html

### Function Plugs

Para atuar como um __Plug__, uma função precisa aceitar uma estrutura de conexão (`%Plug.Conn{}`) e opções. Ela também precisa retornar uma estrutura de conexão. Qualquer função que atenda a esses critérios servirá. 

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

Veja mais detalhes aqui: https://hexdocs.pm/phoenix/plug.html#function-plugs

### Module Plugs

Os __Module Plugs__ são outro tipo de __Plug__ que nos permite definir uma transformação de conexão em um módulo. O módulo só precisa implementar duas funções:

1. `init/1` que inicializa quaisquer argumentos ou opções a serem passadas para `call/2`
2. `call/2` que realiza a transformação da conexão. `call/2` é apenas uma __Function Plug__ que vimos anteriormente.

### Entendendo o fluxo

Um __Plug__ executa antes da __request__ chegar no __controller__. Vamos tentar entender melhor esse fluxo no arquivo de rotas.

Vamos pensar que um usuário fez um post. O navegador acessou a barra de endereço `http://localhost:4000/api/users`, enviou um HTTP Request para a nossa aplicação que estava executando nesse endereço. O HTTP Request foi construído com um par do verbo `POST` e o caminho `/Users`, que foi mapeado para um par do controller `UsersController` e a action `create`. 

Antes de chegar na action `create` do `UsersController`:

1. Primeiramente vai para o `scope "/api"`;
2. depois para o `pipe_throw :api` que redireciona para o bloco `pipeline :api`;
3. depois para o `plug :accepts, ["json"]` dizendo que essa rota aceita o formato `json`,
4. depois no nosso plug `UUIDChecker` que criaremos ainda,
5. e só depois para o `UsersController` onde os dados serão moldados na action create, nesse caso.

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

### O nosso primeiro plug

Crie um arquivo no diretório `lib/rockelivery_web/plugs/uuid_checker.ex`. O conteúdo deste arquivo deve ter a seguinte aparência:

```elixir
defmodule RockeliveryWeb.Plugs.UUIDChecker do
  import Plug.Conn

  alias Ecto.UUID
  alias Plug.Conn

  # init/1 inicializa quaisquer argumentos ou opções a serem passadas para call/2
  def init(options), do: options

  # call/2 é uma Function Plug que realiza a transformação da conexão.
  # Toda Function Plug precisa aceitar uma estrutura de conexão %Plug.Conn{} e opções.
  def call(%Conn{params: %{"id" => id}} = conn, _opts) do
    case UUID.cast(id) do
      :error -> render_error(conn)
      # se o ID for um UUID a conexão (conn) pode ir para o controller:
      {:ok, _uuid} -> conn
    end
  end

  # Caso não tenha id nos paramêtros, como na rota create,
  # continua enviando a conexão no fluxo normal
  def call(conn, _opts), do: conn

  defp render_error(conn) do
    body = Jason.encode!(%{message: "Invalid UUID"})

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(:bad_request, body)
    |> halt()
  end
end
```
Vamos entender algumas funções usadas no código acima:

* `Plug.Conn.put_resp_content_type(conn, content_type)` informa que será devolvido uma resposta no formato json. Veja na documentação: https://hexdocs.pm/plug/Plug.Conn.html#put_resp_content_type/3

* `Plug.Conn.send_resp(conn, status, body)` envia uma resposta com o status e o corpo fornecidos. Veja na documentação: https://hexdocs.pm/plug/Plug.Conn.html#send_resp/3

* `Plug.Conn.halt(conn)` interrompe o pipeline de Plug, evitando que mais plugs downstream sejam invocados. Veja na documentação: https://hexdocs.pm/plug/Plug.Conn.html#halt/1

Em `lib/rockelivery_web/router.ex`:

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

#### Refatorando

Em `lib/rockelivery/error.view` podemos retirar a mensagem de erro do UUID, já que isso está sendo tratado agora no nosso __Plug__ que criamos:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87j670flyct1gk28l0u7.png)

Podemos agora retirar a verificação do UUID nos 3 arquivos onde estava sendo usado:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vvvtniwikqyok9nlhb33.png) 

Exemplo da refatoração no arquivo de update:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkntalulvf7c20m8s9r6.png)
Faça essa refatoração nos outros 2 arquivos.
 
Agora nosso __Plug__ está tratando os IDs inválidos :)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhxiu3agcwad76pvu4h3.png)
 
E assim terminamos a terceira parte da nossa aplicação :)