Como desenvolvi minha primeira API em Spring

Fábio Almeida
9 min readJan 7, 2021

--

O processo de desenvolvimento de uma Interface de Programação de Aplicações (API) hoje em dia pode ser bastante facilitado graças às várias tecnologias encontradas no mercado que nos ajudam abstraindo um monte de código tedioso que é necessário na fabricação de uma API. Isso nos dá mais tempo para focarmos em outras aspectos que são de extrema importância, como por exemplo, a arquitetura, segurança, desempenho e documentação.

Neste post irei descrever como desenvolvi minha primeira API RESTful usando a framework Spring como stack de tecnologia e outros utilitários que facilitaram bastante minha vida nessa jornada.

Entendendo o problema

O problema proposto é desenvolver uma API REST que suporta o processo de abertura de nova conta bancária. Nessa primeira etapa apenas é necessário cadastrar as informações de Nome, Email, CPF e Data de Nascimento dos novos clientes. A tecnologia indicada para desenvolvimento é a Spring + Hibernate. Apesar de já ter um pouco de noção em como funciona uma API REST nunca desenvolvi uma do inicio ao fim, muito menos utilizando o Spring e Hibernate. Como hoje em dia temos a internet para nosso auxílio nada é impossível. Depois de algumas pesquisas encontrei uma série de cursos na Alura que abordava justamente o conteúdo de construção de uma API com Spring Boot. Outros bons materiais que também encontrei foi esse post da Mariana Azevedo sobre boas práticas de programação com Spring e o post do meu amigo Thiago sobre design de API’s modernas. Munido de bom conteúdo em mãos, vou tentar sintetizar o que aprendi.

O começo

Para começar o projeto fiquei conhecendo a plataforma Spring Initializr que ajuda na criação de projetos Spring de forma rápida. Nela configurei as dependências do Spring Web para construir o modelo REST e a arquitetura MVC, o projeto Spring Data JPA para tratar a questão de persistência dos dados no banco de dados através do provedor JPA Hibernate e por fim o Spring Boot DevTools responsável por reiniciar a nossa aplicação de maneira rápida. Com o arquivo gerado só foi necessário importar o projeto para dentro da IDE e executar as dependências pelo Maven para carregar as bibliotecas.

Configuração do Spring Initizalizr para o projeto

O banco de dados

O banco de dados padrão utilizado pelo Spring Boot é o H2. Devido a maior familiaridade acabei alterando para o MySQL. Adicionei a dependência mysql-connector-java ao projeto através do arquivo pom.xml para carregar as bibliotecas responsáveis por fazer a conexão com o banco. Depois iniciei um novo contêiner do banco MySQL no docker e conectei a aplicação Spring ao endereço da imagem passando alguns parâmetros no arquivo application.properties. Acredito que essa abordagem fica mais próxima de um cenário de produção.

Arquitetura e Desenvolvimento

Busquei implantar o estilo arquitetural MVC organizando as classes nas camadas de interface (controller), camada de negócio (service e model) e camada de persistência (repository).

A classe User, na código abaixo irá modelar o objeto da nossa aplicação e foi criada na camada de negócio. Na linha 1, a classe é marcada com a notação @Entitydo Spring Boot para dizer ao JPA que essa classe é relacionada a uma tabela no banco de dados. Ela contêm os atributos id, name, email, cpf, dateOfBirth juntamento com os métodos assessores e construtor. O atributo id é marcado como chave primária pelas notações @Ide geradas pela notação @GeneratedValue

Classe User.java

Para acessarmo esse objeto no banco de dados foi preciso implementar uma interface na camada de persistência que faça uso do repositório JPA. Através do Spring Data podemos construir essa interface herdando da interface JpaRepository. No código abaixo foi criado a interface da classe User passando o tipo da chave primária da classe e também colocado a notação @Repository para indicar que ela é um mecanismo para acesso aos dados.

Interface UserRepository.java

Como boa prática do Spring não é recomendado acessar a camada de persistência direto na camada de interface, para isso é preciso um Service como facilitador no acesso dos Models. Esse intermediador contêm os métodos que serão utilizados pelo Controller para persistência dos dados. Na interface UserService abaixo foi definido apenas o escopo dos métodos. Em alguns métodos foi utilizado o retorno de um objeto do tipo Optional<User> para informar se o usuário está presente ou ausente e dessa forma evitar erros nas rotas.

Interface UserService.java

Já a implementação desses métodos é feita na classe UserServiceImpl abaixo e marcada com a notação @Service. Essa organização é recomendada para garantir um menor acoplamento entre as classes, ou seja, uma classe menos dependente de outra. Aqui é apresentado o famoso conceito de injeção de dependência do Spring. Na linha 5, basicamente a Inversão de Controle (IoC) diz que utilizaremos a interface UserRepository na classe UserServiceImpl. Para isso a Injeção de Dependência (DI) é realizada direto ao método construtor da classe UserServiceImpl. Outras formas encontradas de realizar o DI é nos métodos Gets e Sets e também direto na definição de um atributo.

Classe que de serviço que implementa os métodos UserService

Para acessar tudo que foi desenvolvido até agora é preciso de uma classe controladora. A classe UserController definida abaixo é marcado, nas linhas 1 e 2, com as notações @RestController, que indica ser a controladora de um endpoint, e @RequestMapping que mapeia as requisições para os métodos específicos da classe na URI /v1/users. Na linha 8 fazemos a DI no construtor da classe para acessar a camada de persistência.

Classe controladora do endpoint

Agora começamos a mapear os métodos de requisição Http GET, POST, PUT e DELETE no endpoint /v1/users para ser respondidos pelos seus devidos métodos. O comportamento dos métodos aqui seguem as operações conhecidas por Create, Request, Update e Delete (CRUD) básicas em qualquer API, por isso não irei abordar detalhes da implementação, mas sim levantar outros pontos técnicos que considerei importante.

Caching

O caching é uma técnica utilizada para que os dados de resposta da aplicação fiquem armazenados temporariamente. Dessa maneira, ao realizar uma consulta o servidor verifica a presença do dado no cache e caso encontre retorna a resposta em cache. Essa prática otimiza o uso de recursos do servidor e aprimora o desempenho da API. Para utilizar essa técnica no Spring Boot foi necessário adicionar a dependência spring-boot-starter-cacher no arquivo pom.xml. Depois das bibliotecas carregadas o uso do cache é sinalizado pela notação @Cacheable que indica que a reposta pode ser armazenada. Já a notação @CacheEvict indica que a variável, passada como argumento, deve ser destruída pois os valores armazenados por ela estão desatualizados.

Paginação

A paginação é um importante processo no retorno do conteúdo ao cliente quando temos muitos dados (payload) como resposta. Ela basicamente pega a resposta e divide seu conteúdo em páginas. Através de sua resposta é possível indicar a quantidade total de dados, o método de ordenação, a página atual e quantidade total de páginas, entre outras informações. A notação @PageableDefault indica o padrão de paginação da resposta caso o usuário não especifique seus valores.

A classe DTO

A classe DTO é um importante aliado no retorno das respostas do servidor. Ela é responsável por manipular a resposta das requisições alterando campos considerados sensíveis, como por exemplo senha do usuário, blindando dessa forma as respostas da API. Como no nosso caso não temos dados tão sensíveis acabamos retornando todas as informações da classe User, porém com o incremento do sistema já sabemos onde implementar.

Classe UserDTO.java

A classe Form

A classe Form também é outro importante aliado, só que agora quando manipulamos entrada dos dados na nossa aplicação. Através dessa classe podemos definir apenas os campos necessários para a persistência de um objeto no banco de dados, ignorando os não necessários, como no nosso exemplo o id que é um valor gerado automaticamente pela JPA. É nessa classe que validamos os dados através das notações de Constraints antes de persistir os dados na banco. Restrições importantes como validação de CPF @CPF e data de nascimento com @Past para indicar uma data passada foram usadas na aplicação construída. Para explorar ainda mais o conteúdo acabei implementado uma nova restrição, chamada @ContactEmailConstraint.

Atributos da classe UserForm.java

Novas restrições para validação de e-mail

A ideia nessa restrição é garantir que o e-mail recebido é válido e existente. Não basta ter apenas @, tem que existir! Para fazer essa validação utilizei o provedor de validação da Verifalia que também disponibiliza uma API que pode ser consumida diretamente na nossa aplicação. Seguindo sua documentação fica bem tranquilo a implementação. Basicamente ao receber um e-mail na nossa aplicação esse campo é validado com a notação @ContactEmailConstraint que envia o campo email para o provedor da verifalia. O provedor retorna uma resposta de Success, linha 11, caso o e-mail passe por seus filtros de validação e dessa forma temos a nossa lógica de validação de e-mail interna da nossa API.

Método que válida o e-mail através da API Verifalia

Tratando exceções

Outro aspecto importante que levei em conta na elaboração da API foi o tratamento de exceções nos cenários em que algo dá errado. Nesse intuito a classe ValidationErrorHandler é marcada com a notação @RestControllerAdvice que é acionada quando um erro acontece nas classes da camada de interface. Ao invés de retornar a gigantesca mensagem de erro, é especificado somente o campo que acusou o erro e a mensagem de erro da aplicação, ambas configuradas na classe ErrorDto, dessa forma limpamos a mensagem de erro para um melhor entendimento pelo usuário da API.

Classe responsável por capturar e tratar as exeções

Segurança da API

Durante minhas pesquisas vi que uma questão muito importante quando estamos desenvolvendo uma API é a segurança. Para garantir a integridade dos dados precisamos proteger algumas requisições que podem alterar ou até destruir os mesmos. Uma das características da API REST é o fato de ela ser stateless, ou seja sem armazenar estado, isso significa que a API não guarda o estado de login de um determinado cliente. Uma das maneiras de controlar o acesso aos endpoints da API é através do JSON Web Token e do Spring Security.

Para implementar essa funcionalidade adicionei a dependência do jjwt e do spring-boot-starter-security no pom.xml para carregar as bibliotecas de gerenciamento do token. Na linha 2, foi criado uma nova classe chamada UserApi que implementa a interface UserDetails do Spring Security e contêm os campos de id, email, password e profiles. Essa classe representa o usuário que consumirá a API para manipulação dos dados de abertura de nova conta bancária. Dessa forma, novas interfaces e classes das camadas de interface, negócio e persistência foram criados para manipular o novo modelo. Algumas dessas classes podem ser vistas no código abaixo

Classe UserApi.java que implementa a interface UserDetails
Classe UserApiService.java que acessa a camada de persistência

O método controlador AuthenticationController, mostrado no código abaixo, trata requisições do tipo POST no endpoint /auth que recebem no corpo das requisições valores de e-mail e password. Estes valores são validados e os dados consultados na tabela do banco de dados através de classes do Spring Security nas linhas 16 e 18, se os valores forem válidos um novo token é alocado na linha 19.

Classe AuthenticationController.java

A classe TokenUtil abaixo gerencia a renovação do token para fins de usabilidade. Nas linhas 5 e 7 os parâmetros expiration, que seta o tempo que o token será válido, e o secret, que carrega a chave aleatório para geração do token tem seus valores definidos no arquivo application.properties do Spring Boot. O processo de alocação de um novo token, linha 9, carrega informações do usuário logado, tempo de criação, tempo de expiração e é assinado com o método HS256 e a chave secreta definida. O token alocado e o tipo Bearer são retornados no corpo da resposta ao método POST utilizado.

Classe TokenUtil.java para gerenciamento do token

Na validação do token a requisição lançada é capturada por um middleware antes de ser tratada pelo método responsável na classe controladora. Na linha 3, a classe acionada checa a existência do parâmetro Authorization no cabeçalho da requisição e verifica a presença do token. Já na linha 5, um método é chamado para verificar a validade do token, isto é, se já expirou ou não. Por fim na linha 7, com o token validado, o sistema autentica o usuário e concede acesso aos métodos.

Middleware que captura e busca pela existência dos parâmetros do token

Documentando a API

Com certeza uma das etapas mais importantes em desenvolver uma API é documentá-la para que os usuários saibam como consumir os dados da API. Graças a ferramentas como o Swagger e a biblioteca Spring Fox é possível fazer isso de maneira automatizada. Com rápidas configurações já podemos visualizar a documentação das rotas da nossa API através de interface gráfica abaixo na qual podemos fazer alguns testes manuais.

Uffaaa… Dessa maneira, acredito ter abordado uma boa parte do conteúdo sobre APIs que me trouxe bastaaante conhecimento nesses dias.

O link para o repositório no git pode ser acessado aqui -> https://github.com/fflucas/bank-application

Abraços! 🤞

Referências

1 - Curso Alura: Spring Boot API REST — https://cursos.alura.com.br/course/spring-boot-api-rest

2 - 15 boas práticas para desenvolvimento de aplicações com Spring Boot — https://medium.com/equals-lab/15-boas-pr%C3%A1ticas-para-desenvolvimento-de-aplica%C3%A7%C3%B5es-com-spring-boot-953e0ff2e24f

3 - Design de APIs modernas — https://coderi.com.br/2018/01/11/design-de-apis-modernas/

4 - Validador de e-mails Verifalia — https://github.com/verifalia/verifalia-java-sdk

5 - Spring Boot documentação oficial — https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

6 - Spring Framework documentação oficial — https://docs.spring.io/spring-framework/docs/current/reference/html/

--

--