Pular para o conteúdo principal

Documentando o Caos: REST APIs com Swagger e Spring Boot

Manter a clareza em um ambiente de software moderno é um dos maiores desafios da engenharia.


1. O Cenário dos Microsserviços

Quando se fala em um ambiente de microsserviços, por vezes podemos nos deparar com um cenário caótico. Aplicações com baixo acoplamento e entidades que se comportam separadamente, mas que precisam de orquestração simultânea ou sequencial, exigem um nível de transparência elevado.

Nesse universo, onde as mudanças são frequentes e nem tudo está no mesmo lugar (tal como na era monolítica), manter uma boa documentação das URLs e dos contratos de comunicação com as APIs é essencial. Isso nos permite entender como o ecossistema funciona, nos deixando trabalhar sobre ele sem "atirar no escuro".

Sendo assim, um grande amigo e velho conhecido torna-se essencial: o Swagger.

2. Formas de Utilização

O ecossistema Swagger dispõe de diversas formas de utilização, atendendo desde desenvolvedores independentes até grandes corporações:

  1. Swagger HUB: Versão corporativa. Dispõe de recursos em nuvem com criação de projetos e abertura para desenvolvimento colaborativo.
  2. Embedded Swagger: Pode ser instalado como dependência dentro de projetos, gerando documentação automática a partir do código.
  3. Swagger UI: Biblioteca disponível em javascript, também há imagens docker com a aplicação instalada. Aqui podemos criar um interpretador de endereços da open-api e ter um agregador de swaggers de todos os nossos microsserviços.

3. Implementação no Spring Boot 3

Para projetos modernos com Spring Boot 3, a biblioteca padrão recomendada é a SpringDoc OpenAPI. Ela substitui o antigo Springfox e oferece suporte total ao OpenAPI 3.

3.1. Adicionando a Dependência

No seu arquivo pom.xml, adicione a seguinte dependência:

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>

3.2. Configuração Básica

Por padrão, ao rodar a aplicação, o Swagger UI já estará disponível em: http://localhost:8080/swagger-ui/index.html

Você pode customizar informações da API criando uma classe de configuração:

@Configuration
public class OpenApiConfig {

@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("API de Gestão de Microsserviços")
.version("1.0")
.description("Documentação interativa para orquestração de entidades."));
}
}

3.3. Utilizando Anotações no Controller

Para tornar a documentação mais rica, utilizamos anotações nos nossos endpoints:

Por simplicidade, omitiremos a camada de serviço.
@RestController
@RequestMapping("/v1/users")
@Tag(name = "Users", description = "Endpoint para gestão de usuários do sistema")
public class UserController {

// Dependências...

@Operation(summary = "Listar todos os usuários", description = "Retorna uma lista completa de usuários ativos")
@GetMapping
public List<UserDto> listAll() {
return service.findAllActive();
}
}

3.4 Como fica o resultado final?

Uma vez feitas essas configurações, teremos um swagger com essa cara:

Dashboard inicial do swagger, listando endpoints e contratos de dados

4. Interagindo com a ferramenta

O Swagger nos permite, além de indentificar as APIs disponíveis e seus contratos, fazer requisições sem necessidade de softwares auxiliares, como Postman ou Insomnia. Basta abrir qualquer uma das requisições listadas e preencher os campos necessários.

4.1 Vejamos um GET simples:

Bastando clicar em Try It Out e depois e Execute

Requisição GET simples aberta

Eis o resultado:

Início da resposta da requisição Fim da resposta da requisição, contendo o dto retornado

4.2 Agora um exemplo parametrizado

Para o cenário em questão, teremos um endpoint com uma PathVariable que recebera um username e retornará dados de um usuário específico.

@Operation(summary = "Listar usuário pelo username", description = "Retorna um usuário buscado pelo seu username")
@GetMapping("{username}")
public UserDto getUserByUsername(@PathVariable String username) {
return service.findByUsername(username);
}

Vejamos o exemplo no front-end:

Novo endpoint visível na interface do swagger

Preenchimento do campo disponível na interface

E aqui temos o resultado da nossa requisição:

Retorno da api com requisição

Para métodos POST e PUT um textfield aparecerá esperando o body adequado, como um json ou xml, por exemplo.

5. Alguns pontos importantes

5.1 OpenApi

O swagger que vemos é apenas um front-end, mas a definição dos metadados que são utilizados para construir a página que vemos é fornecida pela open-api, que pode ser conferida pelo endpoint http://localhost:8080/v3/api-docs.

Veremos um JSON com o seguinte formato:

{
"openapi":"3.0.1",
"info":{
"title":"API de Gestão de Microsserviços",
"description":"Documentação interativa para orquestração de entidades.",
"version":"1.0"
},
"servers":[
{
"url":"http://localhost:8080",
"description":"Generated server url"
}
],
"tags":[
{
"name":"Users",
"description":"Endpoint para gestão de usuários do sistema"
}
],
"paths":{
"/v1/users":{
"get":{
"tags":[
"Users"
],
"summary":"Listar todos os usuários",
"description":"Retorna uma lista completa de usuários ativos",
"operationId":"listAll",
"responses":{
"200":{
"description":"OK",
"content":{
"*/*":{
"schema":{
"type":"array",
"items":{
"$ref":"#/components/schemas/UserDto"
}
}
}
}
}
}
}
},
"/v1/users/{username}":{
"get":{
"tags":[
"Users"
],
"summary":"Listar usuário pelo username",
"description":"Retorna um usuário buscado pelo seu username",
"operationId":"getUserByUsername",
"parameters":[
{
"name":"username",
"in":"path",
"required":true,
"schema":{
"type":"string"
}
}
],
"responses":{
"200":{
"description":"OK",
"content":{
"*/*":{
"schema":{
"$ref":"#/components/schemas/UserDto"
}
}
}
}
}
}
}
},
"components":{
"schemas":{
"UserDto":{
"type":"object",
"properties":{
"id":{
"type":"string",
"format":"uuid"
},
"name":{
"type":"string"
}
}
}
}
}
}

Essa api é consumida pelo front-end do Swagger. Sendo assim, é possível utilizar o Swagger-ui para agregar várias REST apis de vários microsserviços num mesmo front-end. Veja um pouco da documentação aqui

5.2 Ambiente produtivo

Dica de Ouro Em ambiente de produção, lembre-se de desabilitar o Swagger ou protegê-lo com autenticação para evitar exposição desnecessária das suas rotas.

Como podemos ver, essa api fica naturalmente exposta e alguns espertinhos pode conseguir explorá-la. Sendo assim, temos algumas formas de lidar com esse problema. Além de autenticações via middleware, podemos simplesmente desabilitar essa feature através de propriedades e variáveis de ambiente.

Vamos setar as propriedades e variávies abaixo no nosso application.yaml:

springdoc:
api-docs:
enabled: ${SPRINGDOC_API_ENABLED:false}
swagger-ui:
enabled: ${SPRINGDOC_SWAGGER_ENABLED:false}

Com a primeira propriedade, desabilitamos a api docs. Com a segunda, desabilitamos apenas o front-end e mantemos a api disponível. Podemos passar esses valores como true apenas em ambientes de desenvolvimento.

6. Por que usar Swagger?

Documentação Viva: Sempre que você altera o código, a documentação se atualiza automaticamente. Sandbox Interativo: Permite testar os endpoints (fazer requisições reais) diretamente pelo navegador. Padronização: Segue a especificação OpenAPI, que é um padrão largamente utilizado na indústria.