Criando APIs com Phoenix 1.7 - Parte 2

Na primeira parte deste artigo, aprendemos como montar o sistema de autenticação para a API utilizando o Phoenix 1.7 e como garantir a segurança dos recursos da API. Agora, na segunda parte, continuaremos desenvolvendo o projeto "my_app" e implementaremos os demais CRUDs da API, que incluem categorias, artigos e comentários. Esses CRUDs estarão relacionados entre si e serão implementados seguindo as boas práticas de desenvolvimento de APIs RESTful. Com a implementação desses CRUDs, a nossa API estará completa e pronta para ser utilizada. Com essa segunda parte, esperamos que você possa compreender como criar uma API RESTful completa e segura com o Phoenix 1.7, seguindo as boas práticas de desenvolvimento.
Guilherme Ferreira | 28 de abril de 2023

Funcionalidades do projeto: CRUDs de categorias, artigos e comentários

Para criar o CRUD de categorias, vamos utilizar o seguinte comando: mix phx.gen.json Blog Category categories name:string

Este comando irá gerar um novo schema chamado Category, que será usado para armazenar as informações das categorias, e o respectivo controlador, as visões e as rotas para lidar com as operações de CRUD.

Após a execução do comando, podemos verificar as mudanças no nosso projeto e realizar a migração do banco de dados para que o schema Category seja criado. Para isso, podemos executar os seguintes comandos:

mix ecto.migrate

###CRUD de Artigos

Para criar o CRUD de artigos, vamos utilizar o seguinte comando:

mix phx.gen.json Blog Article articles title:string body:text user_id:references:users category_id:references:categories

Este comando irá gerar um novo schema chamado Article, que será usado para armazenar as informações dos artigos, e o respectivo controlador, as visões e as rotas para lidar com as operações de CRUD. Neste caso, também estamos relacionando os artigos com os usuários e as categorias.

Após a execução do comando, podemos verificar as mudanças no nosso projeto e realizar a migração do banco de dados para que o schema Article seja criado. Para isso, podemos executar os seguintes comandos:

mix ecto.migrate

CRUD de Comentários

Para criar o CRUD de comentários, vamos utilizar o seguinte comando: mix phx.gen.json Blog Comment comments body:text user_id:references:users category_id:references:categories article_id:references:articles

Este comando irá gerar um novo schema chamado Comment, que será usado para armazenar as informações dos comentários, e o respectivo controlador, as visões e as rotas para lidar com as operações de CRUD. Neste caso, também estamos relacionando os comentários com os usuários, as categorias e os artigos.

Após a execução do comando, podemos verificar as mudanças no nosso projeto e realizar a migração do banco de dados para que o schema Comment seja criado. Para isso, podemos executar os seguintes comandos: mix ecto.migrate

Com a criação dos CRUDs, podemos prosseguir com a configuração das rotas para permitir o acesso aos recursos da API.

Definindo as Rotas de Categorias, Artigos e Comentários

Para adicioná-las, basta usar o resources do Phoenix, que gera automaticamente as rotas necessárias para CRUD (Create, Read, Update, Delete) e mapeia para os métodos correspondentes nos controladores.

No primeiro argumento do resources, informamos o nome da rota, e no segundo, o controlador correspondente. O parâmetro except indica quais rotas devem ser excluídas da geração automática. Neste caso, estamos excluindo as rotas de criação e edição.

As rotas específicas para o sistema de blog incluem a rota de categorias (/categories), a rota de artigos (/articles) e a rota de comentários (/comments).

Para adicioná-las, basta adicionarmos o código abaixo no segundo escopo do arquivo router.ex:

resources “/users”, UserController, except: [:new, :edit]

resources “/categories”, CategoryController, except: [:new, :edit]

resources “/articles”, ArticleController, except: [:new, :edit]

resources “/comments”, CommentController, except: [:new, :edit]

Com essas rotas adicionadas, já podemos criar, ler, atualizar e deletar dados dos schema de Categoria, Artigo e Comentário.

Por exemplo, a rota /categories terá as seguintes rotas geradas automaticamente:

Método HTTP URL Função do controlador Descrição

GET /api/categories index Retorna todas as categorias

GET /api/categories/:id show Retorna a categoria com o id especificado

POST /api/categories create Cria uma nova categoria

PUT /api/categories/:id update Atualiza a categoria com o id especificado

DELETE /api/categories/:id delete Deleta a categoria com o id especificad

Para testar as rotas, é possível usar ferramentas como o Postman ou o cURL, fazendo requisições HTTP para a API em execução. Para iniciar o servidor do Phoenix, basta navegar até a raiz do projeto e executar o comando “mix phx.server”. Assim, o servidor estará pronto para receber as requisições e retornar as respostas correspondentes.

##Testes gerados pelo Phoenix e cobertura de testes

O Phoenix 1.7 possui um sistema de testes integrado que é gerado automaticamente para as nossas aplicações. Quando usamos o comando mix phx.gen.json para gerar o código para o CRUD, o Phoenix cria automaticamente os arquivos de teste necessários para a nossa aplicação.

Os arquivos de teste são criados em uma estrutura de diretórios específica, dentro da pasta test do nosso projeto. O Phoenix gera testes para os schemas, controles e visões, além de testes de integração para cada rota da nossa aplicação.

Para executar os testes gerados pelo Phoenix, basta rodar o comando mix test no terminal, na raiz do projeto. Isso executará todos os testes da aplicação e exibirá o resultado no terminal. Podemos executar testes individuais também, especificando o caminho do arquivo de teste. Por exemplo, para executar apenas os testes para o schema de categoria, podemos executar o comando mix test test/models/category_test.exs.

Além de executar os testes, é importante verificar a cobertura de testes da nossa aplicação. O Phoenix fornece uma ferramenta integrada para isso, chamada ExCoveralls. Essa ferramenta gera um relatório de cobertura de testes que pode ser visualizado em formato HTML.

Para instalar a biblioteca ExCoveralls, adicione-a ao arquivo mix.exs como uma dependência de desenvolvimento:

def deps do
[
{:excoveralls, "~> 0.16.0", only: :test}
]
end

Em seguida, execute o comando mix deps.get para instalar a biblioteca.

Para gerar o relatório de cobertura de testes, você precisa executar os testes com a opção --cover ativada. Por exemplo:

mix test --cover

Isso irá gerar um arquivo de cobertura no formato JSON na pasta cover. Para gerar a cobertura em html prcisamos adicionar ainda mais uma configuração dentro do mix.exs:

def project do
[
app: :my_app,
version: "0.1.0",
elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps(),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test
]
]
end

Repare que adicionamos o trecho de código:

test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test
]

Para gerar um relatório HTML, execute o seguinte comando mix coveralls.html no terminal, na raiz do projeto. Isso irá gerar um arquivo HTML dentro da pasta cover/excoveralls.html do projeto, que pode ser aberto em um navegador web para visualização. O relatório mostra quais partes do código foram cobertas pelos testes e quais partes não foram cobertas. Com isso, podemos identificar áreas da aplicação que precisam de mais testes.

Isso define que o ExCoveralls não será executado automaticamente quando os testes forem finalizados (run_at_exit definido como false), que o arquivo de saída JSON será salvo na pasta cover (json_output_path definido como “cover/excoveralls.json”) e que o relatório HTML será gerado na mesma pasta (html_output_dir definido como “cover”).

Adaptando testes ao sistema de autenticação

Aqui nesse ponto vamos perceber que os novos CRUDs de categoria, artigos e comentários estão com problemas nos testes unitários no nível de controle. Para resolver isso basta adicionar um novo método em: test/support/conn_case.ex

def register_and_log_in_user(%{conn: conn}) do
user = user_fixture()
conn = Guardian.Plug.sign_in(conn, user)
{:ok, token, _claims} = Guardian.encode_and_sign(user)
conn = Plug.Conn.put_req_header(conn, "authorization", "Bearer #{token}")
%{conn: conn, user: user}
end

Lembre de definir os alias corretamente no topo de conn_case.ex:

use ExUnit.CaseTemplate

import MyApp.AccountsFixtures

alias MyApp.Accounts.Guardian

Em seguida basta adicionar esse método nos setups dos testes que você for criar de cruds dentro de áreas restritas como no nosso caso as categoria, artigos e comentários. O código vai ficar assim no caso das categorias:

describe "create category" do
setup [:create_category, :register_and_log_in_user]

Com isso os testes vão funcionar corretamente utilizando a sessão criada pelo register_and_log_in_user.

Conclusão

Neste artigo nas partes 1 e 2, vimos como criar uma API RESTful com o Phoenix 1.7, incluindo autenticação e autorização. Criamos um projeto chamado MyApp, onde implementamos os CRUDs de categorias, artigos e comentários, todos relacionados entre si. Utilizamos o banco de dados PostgreSQL, configurado com o Docker, para persistir nossos dados.

Mostramos também como criar o sistema de autenticação para permitir o acesso à API apenas para usuários autenticados, e como configurar todas as rotas necessárias para os nossos CRUDs. Além disso, abordamos a geração de testes automatizados e como verificar a cobertura de testes.

Esperamos que este artigo tenha sido útil para você que deseja aprender mais sobre o Phoenix e a construção de APIs com Elixir. A ateliware ama trabalhar com Elixir e o Phoenix em seus projetos, e está sempre de portas abertas para programadores interessados em explorar essas tecnologias. Se você se identifica com a nossa cultura e gostaria de fazer parte da nossa equipe, entre em contato conosco!

Guilherme Ferreira
Software Engineer formado em Ciência da Computação. Tutor de cursos e desenvolvedor de software. Curte explorar novas tecnologias e estudar inteligência artificial.