💧🦅 Elixir: O Ciclo de Vida do Request no Phoenix
Tags: elixir, phoenix, braziliandevs
Published: 2021-05-27 22:43:58

O objetivo deste artigo é falar sobre o __Ciclo de Vida do Request__ no framework web para a Linguagem de Programação Elixir chamado __Phoenix__. Esse assunto é introdutório para começarmos a aprender o framework, onde criaremos duas páginas ao longo deste mini projeto.

Como base para este guia, foram retirados alguns trechos da documentação oficial do Phoenix, de algumas ideias do bootcamp da Rocketseat e do blog netguru.com.

O código final deste projeto você pode encontrar no meu github: https://github.com/maiquitome/hello_phoenix_dev

# Criando o Projeto

Primeiramente, vamos criar o projeto rodando o comando abaixo:

```bash
$ mix phx.new hello_phoenix --no-ecto
```

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

Note que usamos um underline para separar o nome `hello_phoenix`. Essa separação chamada de `snake case` é uma convenção da comunidade Elixir, usada bastante também para nomear arquivos. Com o underline, nossa aplicação vai conseguir converter o nome `hello_phoenix` para `HelloPhoenix` em nomes de módulos.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ua9xjc0837dvz8653iz.png)
 
Note também que usamos a flag `--no-ecto`, isso diz ao Phoenix que __não__ queremos usar um banco de dados nessa aplicação.

Vamos entrar agora na pasta do projeto com o comando `cd hello_phoenix` e subir o servidor com o comando `mix phx.server`. Vamos acessar no navegador o endereço `http://localhost:4000/` e você deverá ver a tela de Boas-vindas do Phoenix:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj3ydfalqh1w46yjmqru.png)

Se você não tem ainda o Elixir e o Phoenix instalado, criei um artigo falando sobre isso: [Instalação das ferramentas de um desenvolvedor Elixir](https://dev.to/maiquitome/instalacao-das-ferramentas-de-um-desenvolvedor-elixir-1bjm)

# Uma Nova Página

Quando seu navegador acessa `http://localhost:4000/` na barra de endereço, ele envia uma __solicitação HTTP (HTTP request)__ para qualquer serviço que esteja sendo executado nesse endereço, neste caso, nossa aplicação Phoenix.

A __solicitação HTTP__ é feita de __um verbo e um caminho__. Por exemplo, as seguintes solicitações do navegador são traduzidas em:

| BARRA DE ENDEREÇO DO NAVEGADOR    | VERBO | CAMINHO      |
| ---------------------------------:|:-----:| ------------:|
| http://localhost:4000/            | GET   | /            |
| http://localhost:4000/hello       | GET   | /hello       |
| http://localhost:4000/hello/world | GET   | /hello/world |

Existem outros verbos HTTP. Por exemplo, o envio de um formulário onde normalmente usamos o verbo `POST`.

Os aplicativos da Web normalmente lidam com solicitações mapeando cada __par de verbo / caminho__ em uma parte específica de seu aplicativo. Essa correspondência no Phoenix é feita pelo __roteador (router)__. Por exemplo, podemos mapear "/articles" para uma parte de nosso aplicativo que mostra todos os artigos. Portanto, para adicionar uma nova página, nossa primeira tarefa é adicionar uma nova rota.

### Uma Nova Rota

O roteador mapeia pares de __verbo HTTP / caminho__ exclusivos para pares de __controlador / ação__ que os tratarão. Os __controladores (controllers)__ em Phoenix são simplesmente módulos Elixir. __Ações (actions)__ são funções definidas nesses controladores.

O Phoenix gera um arquivo de `roteador` para nós em novos aplicativos no caminho `lib/hello_phoenix_web/router.ex`. É aqui que vamos encontrar a rota de Boas-Vindas do Phoenix.

O percurso do nosso "Welcome to Phoenix!", quando acessamos antes o endereço `http://localhost:4000/` tem esta aparência.

```elixir
get "/", PageController, :index
```

Vamos digerir o que essa rota está nos dizendo. Visitando `http:// localhost:4000/` emita uma __solicitação HTTP GET__ (HTTP GET request) para o caminho raiz. Todas as solicitações como essa serão tratadas pela __função index__ no módulo `HelloPhoenixWeb.PageController` definido em `lib/hello_phoenix_web/controllers/page_controller.ex`.

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

A página que iremos construir dirá simplesmente "Hello World, from Phoenix!" quando apontaremos nosso navegador para o endereço `http://localhost:4000/hello`.

A primeira coisa que precisamos fazer para criar essa página é definir uma rota para ela. Vamos abrir o arquivo `lib/hello_phoenix_web/router.ex` em um editor de texto. Para um aplicativo totalmente novo, o arquivo é semelhante:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b0n9hghnnniy58z1kiol.png)
 
Esse `scope "/"` simplesmente informa que todos os caminhos que estiverem nesse escopo (nesse bloco de código), serão colocados após o endereço `http://localhost:4000`. Outro exemplo, se tivéssemos outro bloco de código abaixo com `scope "/api"`, então todos os caminhos que estivessem nesse escopo, seriam colocados após o endereço `http://localhost:4000/api`.

```elixir
scope "/", HelloWeb do
  # por enquanto, não se preocupe com essa linha abaixo
  pipe_through :browser

  get "/", PageController, :index

  # aqui adicionamos a nova rota
  # http://localhost:4000/hello
  get "/hello", HelloController, :index
end
```
### Um Novo Controller

Os __controladores__ são módulos Elixir e as __ações__ são funções Elixir definidas neles. O objetivo das ações é reunir todos os dados e realizar as tarefas necessárias para a renderização. Em nossa nova rota precisamos de um módulo `HelloPhoenixWeb.HelloController` com uma ação `index/2`.

Para que isso aconteça, vamos criar o arquivo `lib/hello_phoenix_web/controllers/hello_controller.ex` e torná-lo parecido com o seguinte:
```elixir
defmodule HelloPhoenixWeb.HelloController do
  # não se importe muito agora com a linha abaixo, apenas
  # saiba que isso define o nosso módulo como um Controller
  use HelloPhoenixWeb, :controller

  def index(conn, _params) do
    text(conn, "Hello World, from Phoenix!")
  end
end
```
Ao acessarmos agora o endereço `http://localhost:4000/hello` no navagador, devemos ver a nossa mensagem. (Caso você tenha interrompido o servidor ao longo do caminho, a tarefa de reiniciá-lo é `mix phx.server`.)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uck0xk5lm8bz02qbi7ox.png)
Bem, você deve estar se perguntando, o que é esse `conn` e esse `_params`? essa função `text` tá vindo de onde? Pois bem, vamos agora entrar em detalhes para entender tudo isso.

Todas as __ações do controlador__ levam dois argumentos. O primeiro é a conexão `conn`, sendo uma `struct` que contém muitos dados sobre a solicitação. O segundo, `params`, são os parâmetros da solicitação, ou seja, são dados passados junto da rota. Aqui, não estamos usando `params` ainda, então evitamos os avisos do compilador adicionando o underline `_`.

Vamos ver a variável `conn` com mais detalhes. Para isso, vamos modificar o código adicionando `IO.inspect()`:
```elixir
defmodule HelloPhoenixWeb.HelloController do
  use HelloPhoenixWeb, :controller

  def index(conn, _params) do
    conn
    |> IO.inspect()
    |> text("Hello World, from Phoenix!")
  end
end
```
Agora, atualizando a página `http://localhost:4000/hello`, podemos ver no terminal que a variável `conn` tem o valor `%Plug.Conn{}`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjy5hyzq9f8g8tp5eq61.png)

Se você não tem muita familiaridade com o __Pipe Operator__ `|>` indico acessar esse guia para ter total entendimento do código: https://elixirschool.com/pt/lessons/basics/pipe-operator/

Antes de entender a variável `params`, vamos ver de onde está vindo a função `text`. Ela pertence ao módulo `Phoenix.Controller`, mas como já temos todas as funcionalidades do controller no nosso módulo, pelo motivo que colocamos a linha `use HelloPhoenixWeb, :controller`, não precisamos chamar a função com o módulo dessa forma `Phoenix.Controller.text()`.

Mas por curiosidade, se colocarmos dessa maneira `Phoenix.Controller.text()` e repousarmos a seta do mouse sobre a função, podemos verificar informações de entrada, retorno e exemplos de uso:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fhy2vjimeypngu81onn.png)
Lembrando que essa funcionalidade de passar o mouse em cima faz parte da extensão do vscode __ElixirLS__. Eu falo sobre essa e outras ferramentas nesse artigo: [Instalação das ferramentas de um desenvolvedor Elixir](https://dev.to/maiquitome/instalacao-das-ferramentas-de-um-desenvolvedor-elixir-1bjm)

Uma última coisa antes de partirmos para o entendimento do `params` é criar uma __visualização (view)__. 

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

Para isso acontecer precisamos alterar o nosso código do controller novamente:

```elixir
defmodule HelloPhoenixWeb.HelloController do
  use HelloPhoenixWeb, :controller

  def index(conn, _params) do
    # aqui trocamos a função `text` para a função `render`
    render(conn, "index.html")
  end
end
```

Agora ficou mais fácil você adivinhar de onde está vindo a função `render`, não é mesmo? se você pensou no módulo `Phoenix.Controller` você acertou!

O núcleo da action index agora é `render(conn, "index.html")`, que diz ao Phoenix para renderizar o "index.html". Os módulos responsáveis ​​pela renderização são as views. Por padrão, as views do Phoenix têm o nome do controlador, portanto, o Phoenix espera que exista um `HelloPhoenixWeb.HelloView` e que manipule o "index.html" para nós.

*Nota: Usar um átomo como o nome do modelo (template) também funciona* `render(conn, :index)`*. Nestes casos, o template será escolhido com base nos Accept headers, por exemplo,* `"index.html"` *ou* `"index.json"`.

### Uma Nova View 

As views do Phoenix atuam como a camada de apresentação. Por exemplo, esperamos que a saída da renderização de "index.html" seja uma página HTML completa. Para tornar nossas vidas mais fáceis, costumamos usar templates para criar essas páginas HTML.

Vamos criar uma nova view. Crie um arquivo com o diretório `lib/hello_phoenix_web/views/hello_view.ex` e faça com que tenha a seguinte aparência:

```elixir
defmodule HelloPhoenixWeb.HelloView do
  use HelloWeb, :view
end
```

Agora, para adicionarmos templates a esta view, simplesmente precisamos adicionar arquivos ao diretório `lib/hello_phoenix_web/templates/hello`. Observe que o nome do controlador (HelloController), o nome da view (HelloView) e o diretório do template (hello) seguem a mesma convenção de nomenclatura e são nomeados um após o outro.

Um arquivo de template tem a seguinte estrutura: `NAME.FORMAT.TEMPLATING_LANGUAGE`. Em nosso caso, criaremos um arquivo "index.html.eex" em 
"lib/hello_phoenix_web/templates/hello/index.html.eex". ".eex" significa `EEx`, que é uma biblioteca para incorporar Elixir que é enviado como parte do próprio Elixir. Phoenix aprimora `EEx` para incluir escape automático de valores. Isso o protege de vulnerabilidades de segurança, como Cross-Site-Scripting, sem nenhum trabalho extra da nossa parte.

Crie `lib/hello_web/templates/hello/index.html.eex` e faça com que tenha a seguinte aparência:

```elixir
<div class="phx-hero">
  <h2>Hello World, from Phoenix!</h2>
</div>
```
Inspecionando a página conseguimos ver a classe "phx-hero": 
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aw2hni4jcvgt4mm6v3r2.png)
 
 
Agora que temos a rota, o controlador, a view e o template, devemos ser capazes de apontar nossos navegadores para `http://localhost:4000/hello` e ver nossa saudação do Phoenix! (Caso você tenha interrompido o servidor ao longo do caminho, a tarefa de reiniciá-lo é `mix phx.server`.)

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

Há algumas coisas interessantes a serem observadas sobre o que acabamos de fazer. Não precisemos parar e reiniciar o servidor enquanto fazíamos essas alterações. Sim, o Phoenix tem __hot code reloading!__ Além disso, embora nosso arquivo `index.html.eex` consistisse em apenas uma única tag div, a página que recebemos foi um documento HTML completo. Nosso modelo de índice é renderizado no layout do aplicativo - `lib/hello_web/templates/layout/app.html.eex`. Se você abri-lo, verá uma linha semelhante a esta:

```elixir
<%= @inner_content %>
```
que injeta nosso template no layout antes que o HTML seja enviado para o navegador.

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

Agora sim! Com o assunto sobre view já abordado, podemos partir para a construção da segunda página onde usaremos a variável `params` para entendermos ela melhor. 

# A Segunda Página

Vamos adicionar uma nova página que irá reconhecer um pedaço da URL, que será o nome de uma pessoa, atribuir esse valor "nome" a chave "person", e passar essa chave e valor para o controlador, capturando para dentro da variável `params`, e passando para um template, para que a pessoa possa dizer olá.

Lembrando que, para adicionar uma página, precisamos criar uma rota. 

### A Segunda Rota

Para criar a segunda rota, vamos usar o mesmo `HelloController` de antes, mas dessa vez com uma action diferente, que será nomeada como `show` ao invés de `index`.

```elixir
scope "/", HelloPhoenixWeb do
    pipe_through :browser

    get "/", PageController, :index
    get "/hello", HelloController, :index
    
    # aqui adicionamos uma nova rota
    # http://localhost:4000/hello/nome_da_pessoa
    get "/hello/:person", HelloController, :show
end
```

Observe que usamos a sintaxe `:person` no caminho. O Phoenix pegará qualquer valor que apareça nessa posição da URL e o converterá em um parâmetro (chave e valor). Por exemplo, se apontarmos o navegador para: `http://localhost:4000/hello/Maiqui`, o valor da chave "person" será "Maiqui".

### A Segunda Action

As solicitações (requests) para nossa nova rota serão tratadas pela action `show` no `HelloPhoenixWeb.HelloController`. Já temos o controller em `lib/hello_phoenix_web/controllers/hello_controller.ex`, então tudo o que precisamos fazer é editar esse arquivo e adicionar uma action `show` a ele. 

Agora como usaremos a variável `params`, __não__ precisamos mais ter que usar o sinal `_` na frente dela. Como no primeiro exemplo da action `index`, vamos usar a função `text` para mais rapidamente já podermos enxergar na tela do nosso navegador o conteúdo de `params` sem precisarmos de um template. 

```elixir
def show(conn, params) do
  text(conn, params)
end
```

Acessando o endereço http://localhost:4000/hello/Maiqui veremos um erro de tipo. A função `text` espera um tipo `String`, mas recebeu um `map` com a chave "person" e o valor "Maiqui".

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

Se olharmos no terminal, também veremos os parâmetros:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n707nmevfybfjk4c31d6.png) 

Podemos converter o map `params` para um JSON (JavaScript Object Notation), que também é um formato de chave e valor, usado bastante para troca de informações entre sistemas web. A função `Phoenix.Controller.json()` consegue converter automaticamente um map para JSON (OBS: para structs existem alguns detalhes para funcionar, mas não vamos nos preocupar com isso agora). Então vamos trocar a funcão `text` pela função `json`.

```elixir
def show(conn, params) do
  json(conn, params)
end
```
Acessando o endereço http://localhost:4000/hello/Maiqui veremos agora nossos dados no formato JSON:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nduqbhbhfkfofmcgpyq9.png) 
 
Podemos instalar uma extensão do chrome chamada [JSON Viewer](https://chrome.google.com/webstore/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh?hl=pt-BR). Essa extensão é muito útil quando temos uma grande quantidade de dados para visualizar. Com ela instalada, nossa tela terá essa aparência:

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

Agora que sabemos o conteúdo de `params`, podemos partir para a construção do segundo template.

### O Segundo Template

Vamos agora alterar o código da nossa action `show`. Adicionaremos a função `render` e passaremos três argumentos para ela: a conexão `conn`, o template `"show.html"`, e um par de chave/valor onde `:person_name` é a chave, e `params["person"]` é o valor.

```elixir
def show(conn, params) do
  render(conn, "show.html", person_name: params["person"])
end
```

Para a última peça deste quebra-cabeça, precisaremos de um novo template. Como é para a action `show` do `HelloController`, ele irá para o diretório `lib/hello_web/templates/hello` e será chamado de `show.html.eex`. Ele se parecerá com o nosso primeiro template `index.html.eex`, exceto que precisaremos exibir o nome da pessoa.

Para fazer isso, usaremos as tags especiais `EEx` para executar expressões Elixir - `<%= %>`. Observe que a tag inicial tenha um sinal de igual como este: `<%=`. Isso significa que qualquer código Elixir que estiver entre essas tags será executado e o valor resultante substituirá a tag. Se o sinal de igual estiver faltando, o código ainda será executado, mas o valor não aparecerá na página.

E é assim que o template deve ser:

```elixir
<div class="phx-hero">
  <h2>Hello World, from <%= @person_name %>!</h2>
</div>
```
O nome da pessoa aparece como `@person_name`. Chamamos de "atribuições" ("assigns") os valores passados ​​do controller para as views. É um bit especial de sintaxe metaprogramada que representa `assigns.person_name`. Se você colocar `assigns.person_name` no lugar de `@person_name` também vai funcionar, mas `@person_name` é muito mais agradável para os olhos e muito mais fácil de trabalhar em um template.

Agora se você apontar seu navegador para http://localhost:4000/hello/Maiqui, deverá ver uma página semelhante a esta:

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

Neste momento, podemos partir para uma pequena refatoração na action `show` usando o nosso querido __Pattern Matching__ do Elixir. Lembra que temos como paramêtro um map `%{"person" => "Maiqui"}`? Então, podemos fazer a atribuição da variável `name` na definição da função. Isso lembra um pouco a desestruturação na Linguagem de Programação JavaScript.

```elixir
def show(conn, %{"person" => name}) do
  render(conn, "show.html", person_name: name)
end
```

# Sobre o Endpoint

Todas as solicitações HTTP (HTTP requests) começam em nosso __endpoint__ do aplicativo. Você pode encontrá-lo como um módulo denominado `HelloPhoenixWeb.Endpoint` em `lib/hello_phoenix_web/endpoint.ex`. Depois de abrir o arquivo do endpoint, você verá que, semelhante ao roteador, o endpoint tem muitas chamadas para `plug`. __Plug__ é uma biblioteca e especificação para unir aplicativos da web. É uma parte essencial de como o Phoenix lida com requests.

Por enquanto, basta dizer que __cada Plug define uma parte do processamento da request__. No endpoint, você encontrará um esqueleto mais ou menos assim:

```elixir
defmodule HelloPhoenixWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :demo

  plug Plug.Static, ...
  plug Plug.RequestId
  plug Plug.Telemetry, ...
  plug Plug.Parsers, ...
  plug Plug.MethodOverride
  plug Plug.Head
  plug Plug.Session, ...
  plug HelloPhoenixWeb.Router
end
```

Podemos ver que o último `plug` é o módulo `HelloPhoenixWeb.Router`. Então, a request passa por todos esses `plugs` antes de chegar no módulo `HelloPhoenixWeb.Router`. O endpoint contém o caminho comum e inicial pelo qual todas as requests passam. Se você quiser que algo aconteça em todas as requests, vá para o endpoint.


# Um Resumão do Ciclo de Vida do Request

1. Um usuário insere uma URL. O navegador cria uma solicitação (request) e a envia pelo protocolo HTTP/HTTPS.

2. A estrutura de conexão denominada `conn` é criada pelo adaptador Plug Cowboy do servidor HTTP baseado em Erlang chamado Cowboy. Essa conexão será manipulada e transformada nas próximas etapas para gerar uma resposta. 

3. A request chega ao Endpoint, onde vários plugs executam algum tipo de processamento. Em muitos aplicativos da web, provavelmente escreveríamos um plug para verificar se o usuário está autenticado e - com base nisso - mostraríamos ao usuário um recurso ou redirecionaríamos para a página de login.

4. O roteador (router) encaminha um par de `verbo/caminho` da request para um controlador (controller) onde será executada uma ação (action). Além disso, aqui definimos plugs e pipelines específicos (que são conjuntos de plugs) pelos quais as requests devem passar.

5. No controller são recuperadas as informações da request, dados são buscados no banco de dados, serviços são chamados para executar a lógica de negócio, renderizar um modelo (template) específico ou renderizar um JSON. Resumindo, dados são preparados para a camada de apresentação. 

6. A view lida com os dados estruturados do controller e os converte em uma apresentação a ser mostrada aos usuários.

7. No template é gerado o HTML que deve ser adicionado como corpo à resposta (response).

8. Quando a view renderiza o template, ela chama a função `send_resp` para retornar uma resposta HTTP (HTTP response) ao cliente. 

9. Finalmente, o usuário está vendo dos dados retornados pelo nosso aplicativo web!

# Conclusão

Busquei trazer aqui meus primeiros passos no aprendizado do Phoenix. Tenho aprendido bastante escrevendo ele e espero que possa ter te ajudado de alguma forma. Existem vários assuntos que vimos para serem analisados com mais profundidade. Conforme irei progredindo estarei escrevendo mais artigos. É notório que é ensinando que se aprende. Se algo que você leu lhe pareceu confuso e você tiver uma definição melhor do assunto, ou tiver alguma dúvida e eu puder ajudar, por favor compartilhe comigo, estou sempre no [Linkedin](https://www.linkedin.com/in/maiquitome/) ou deixe sua mensagem nos comentários :)