Cursos que formaram meu caráter: Desenvolvimento web com Quarkus - API First com o OpenAPI Generator
Tags: gradle, jaxrs, openapi, quarkus
Published: 2022-12-15 04:53:30

---
title: Cursos que formaram meu caráter: Desenvolvimento web com Quarkus - API First com o OpenAPI Generator
published: true
description: O que aprendi com cursos realizados e como apliquei outras técnicas conhecidas para evoluí-los
tags: gradle, jaxrs, openapi, quarkus
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m8uazy3rfnxc4ha6mn6r.jpg
# published_at: 2022-12-14 21:51 +0000
---

> "Imaginação é o começo da criação. Você imagina o que deseja, deseja o que imagina e, por fim, cria o que deseja". George Bernard Shaw

Neste quinto post falaremos sobre um tema que estudo desde 2012 quando tive uma grande experiência com **SOA (Service Oriented Architecture)**.

Falaremos sobre pontos positivos e negativos da abordagem, uma vez que tudo tem trade-offs.

Para trabalhar com [Quarkus](https://quarkus.io/) vamos precisar usar o **JAX-RS** como ponte para geração de nossos contratos. Como grande fã do [Spring](https://spring.io/) vamos entrar em algumas discussões sobre estar ou não atrelado à especificação do **Java**. Vamos então comparar vantagens e desvantagens de se utilizar o **JAX-RS** ou abstrações **Spring**.

{% embed https://giphy.com/gifs/latenightseth-seth-meyers-l0Iy00xfeHKouH65G %}

Prefiro a solução com **Spring**. E irei explicar o porque.

Esse artigo faz parte de uma série, abaixo é possível encontrar a [lista completa de artigos](#serie_completa).

Estamos nos baseando no curso [Desenvolvimento web com Quarkus](https://www.udemy.com/course/des-web-quarkus) do @viniciusfcf.

O repositório que estamos utilizando é:

{% embed https://gitlab.com/arthurfnsc-udemy/desenvolvimento-web-quarkus/ifood/ %}

---

## SOA so far

Caso você tenha interesse em outros posts sobre **SOA**, escrevi alguns insights que podem ser úteis:

* [Desacoplamento em APIs com API First](https://medium.com/@arthurfnsc/desacoplamento-em-apis-com-api-first-1c9eac93e41e)
* [Fundamentos, Objetivos e Benefícios estratégicos de SOA (parte 1)](https://medium.com/@arthurfnsc/fundamentos-objetivos-e-benef%C3%ADcios-estrat%C3%A9gicos-de-soa-parte-1-19fe0f229f51)
* [Fundamentos, Objetivos e Benefícios estratégicos de SOA (parte 2)](https://medium.com/@arthurfnsc/fundamentos-objetivos-e-benef%C3%ADcios-estrat%C3%A9gicos-de-soa-parte-2-1b630419a2d1)
* [Princípios SOA](https://medium.com/@arthurfnsc/princ%C3%ADpios-soa-57d0c34d9f0f)
* [Não confunda alhos com bugalhos: SOA e SOAP](https://medium.com/@arthurfnsc/n%C3%A3o-confunda-alhos-com-bugalhos-soa-e-soap-359ca3f32813)
* [Contract-First em REST com Swagger](https://arthurfnsc.medium.com/contract-first-em-rest-com-swagger-859ff0567a1)
* [Contract-First em REST](https://arthurfnsc.medium.com/contract-first-em-rest-aba428f0eff1)

Em 2012 entrei em um instituto de pesquisas, o [IBTI](https://www.ibti.org.br/). Um instituto que foi bem importante para as minhas concepções arquiteturais.

Entrei em um laboratório de **SOA** e me lembro de pensar que se tratava de um plugin do Java na época.

**SOA** me influenciou tanto que um de seus princípios, a [Padronização de contrato de serviços](https://patterns.arcitura.com/soa-patterns/basics/serviceorientation/standardized_service_contract) acabou me atrasando a entrar no mundo **REST**. Para mim, se eu não utilizasse **Contract-First** não estava bom.

Me lembro inclusive de um projeto que escrevi uma vez, que eu tinha definido um projeto multi módulo em que um módulo trabalhava com **REST** e esse chamava outro módulo que trabalhava com **SOAP** só porque eu utilizava **Contract First** lá.

![Face palm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzz7cjpvswb7nxih3jw2.png)

Fico pensando hoje no overhead dessa decisão arquitetural.

São coisas que olho para trás e apesar de não concordar hoje, sei que me ajudaram a chegar aonde estou hoje.

Como dito anteriormente, em um instituto de pesquisas o que é necessário fazer é experimentar novas coisas. E experimentar quer dizer provar coisas que você não sabe se estão certas ou não.

## OpenAPI abordagens

Partirei do pré-suposto que você já ouviu falar sobre [OpenAPI](https://www.openapis.org/) ou [Swagger](https://swagger.io/) daqui para frente.

Como abordagens de APIs com **Swagger** ou **OpenAPI** podemos dividir em 3 abordagens:

### Escrita de contrato e implementação em uma linguagem de programação

Nesse abordagem é definido um contrato e a equipe de desenvolvimento segue a especificação em seu código de uma forma de-para.

### Geração de contrato a partir de código

Nessa abordagem iniciamos por nosso código para gerar o nosso contrato.

### Geração de código a partir de contrato

Nessa abordagem iniciamos por nosso contrato para a geração de um código. Essa abordagem é conhecida como **Contract-First**.

## Prós e contra de cada abordagem

Na primeira abordagem o principal problema que possuímos é o fato de a implementação ser manual, sendo assim, o programador pode se confundir ou esquecer de algo da especificação.

Na segunda abordagem o principal problema no caso do **Java** são questões com a serialização e desserialização de classes que especificamos. Podemos utilizar classes que não implementam serialização para gerar nosso **Swagger** ou **OpenAPI**.

Ainda na segunda abordagem, a vantagem está em não estarmos atrelados a outras soluções e utilizar implementações que a maioria da comunidade utiliza.

Na terceira abordagem o principal problema é estar atrelado a alguma solução, que pode não ter todas as funcionalidades que precisamos.

Ainda na terceira abordagem, a vantagem é estarmos sempre alinhados a um contrato especificado, evitando a quebra o mesmo.

Nesse post iremos utilizar a terceira abordagem, mesmo tendo o problema de estar atrelado a uma solução de geração de código.

Já escrevi sobre o [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator), porém, precisei fazer alguns ajustes para trabalhar com isso no **Quarkus** e é sobre isso que vamos iniciar agora.

Mais à frente falaremos sobre outros problemas dessa abordagem.

---

## Principais configuração em nosso projeto

Podemos ver a configuração utilizando **API-First** em nosso projeto nos arquivos **applications/cadastro/build.gradle**:

```groovy
apply from: "$rootDir/plugins/openapigen_cadastro.gradle"
```

E no arquivo **plugins/openapigen_cadastro.gradle**:

```groovy
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask

buildscript {
    repositories {
        mavenLocal()
        maven { url "https://repo1.maven.org/maven2" }
    }
    dependencies {
        classpath "org.openapitools:openapi-generator-gradle-plugin:$openApiGenVersion"
    }
}

apply plugin: 'org.openapi.generator'

def apiServerOutput = "$buildDir/generated/openapi-code-server".toString()

task generateApiServer(type: GenerateTask) {
    generatorName = "jaxrs-spec"
    inputSpec = "$projectDir/src/main/resources/openapi/restaurantes_v1.yml".toString()
    outputDir = apiServerOutput
    apiPackage = "org.openapi.server.v1.restaurantes.api"
    invokerPackage = "org.openapi.v1.restaurantes.invoker"
    modelPackage = "org.openapi.v1.restaurantes.model"
    configOptions = [
        "dateLibrary"            : "java8",
        "hideGenerationTimestamp": "true",
        "interfaceOnly"          : "true",
        "performBeanValidation"  : "true",
        "returnResponse"         : "true",
        "serializableModel"      : "true",
        "useBeanValidation"      : "true",
        "useOptional"            : "true",
        "useSwaggerAnnotations"  : "false"
    ]
}

compileJava.dependsOn(
    generateApiServer
)

sourceSets.main.java.srcDir "$apiServerOutput/src/gen/java"
```

Na configuração **apiServerOutput** definimos um destino para no qual serão criadas nossas classes com base em nossa **OpenAPI**.

Na configuração do plugin definimos que utilizaremos o **JAX-RS** como especificação de nossa API. Lembrando que existem [outras abordagens](https://openapi-generator.tech/docs/generators/) para o **generatorName** tanto para **server** quanto para **client**.

Na configuração **inputSpec** utilizamos o caminho para nossa **OpenAPI** no projeto.

**apiPackage**, **invokerPackage** e **modelPackage** são as configurações de nossos packages da aplicação que será gerada:

![Package configurations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tiuhrwu3rpp8c9lmsk0u.png)
 
Sobre as configurações utilizadas em **configOptions do jaxrs-spec** podemos encontrar mais detalhes no [seguinte link](https://openapi-generator.tech/docs/generators/jaxrs-spec/).

Definimos então as bibliotecas do **java8** para configuração de nossas bibliotecas de data. Não se confunda com o fato de o **Java** possuir outras versões para além do 8! Para o plugin isso se refere ao fato de usarmos **LocalDate**, classe que apareceu a partir do **Java 8**.

**hideGenerationTimestamp** diz respeito à geração do timestamp de geração estar na interface gerada. Como estamos gerando nossas classes no package **build** essa é uma configuração que não impacta muito na forma como usamos o plugin.

**interfaceOnly** diz respeito à geração de interfaces para serem implementadas.

Para nossa abordagem não precisaremos de classes concretas, porque iremos implementar a interface, mas para entender o que mudaria no caso desse parâmetro ser false, pense nos projetos gerados a partir do site [https://editor.swagger.io/](https://editor.swagger.io/) > Generate Server > jaxrs-spec. Nesse caso será criado uma classe concreta para ser evoluída.

Para entender nossa abordagem, estamos gerando uma interface **Java** com base em um contrato **OpenAPI**, essas classes geradas não precisarão ser versionadas, pois podem ser criadas e recriadas dado evolução da nossa API. O que buscamos é ter a certeza que estamos implementando todos os métodos da interface, e caso mudemos nossa **OpenAPI** perceber que precisamos estar em compliance com o contrato descrito.

![Generate Server](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okfkvch7frkcrrl2vmom.png)

A configuração **performBeanValidation** e **useBeanValidation** dizem respeito a utilizar o **Bean Validation** ou não nas classes geradas, bem como realizar sua validação em camada de **Controller**. Isso inclusive diz respeito a uma diferença da abordagem do @viniciusfcf. Estamos delegando ao plugin essa validação, porém isso nos trás um problema, não estamos mais controlando o Validation das nossas classes, e com isso estamos atrelados aos lados bons e ruins do plugin. Na solução utilizada pelo Vinícius, ele conseguia ajustar coisas em seus **DTOs**, no nosso caso estamos delegando isso às classes de modelo geradas pelo plugin. Existem até formas de sobrescrevermos como o plugin gera as classes com o [Mustache](https://mustache.github.io/), mas para fins desse post não pensei em criar configurações próprias, preferi utilizar a geradas pelo próprio plugin.

A configuração **returnResponse** é uma das principais reclamações que tenho com relação ao **JAX-RS**. Caso essa solução seja definida como false os métodos gerados terão em sua assinatura as classes da especificação e não a classe **javax.ws.rs.core.Response**. As vantagens do **returnResponse** false é a geração de métodos como:

```java
@Path("/restaurantes")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen")
public interface RestaurantesApi {

...

@POST
    @Consumes({ "application/json" })
    void cadastraRestaurante(@Valid CadastroRestaurante cadastroRestaurante);

@GET
    @Path("/{idRestaurante}/pratos")
    @Produces({ "application/json" })
    List<Prato> recuperaPratosRestaurante(@PathParam("idRestaurante") Long idRestaurante);

...
}
```

### Os primeiros "problemas" com OpenAPI e JAX-RS

A desvantagem inicial que vi foi no retorno de **HTTP Status** para **201 Created**, pois no caso de métodos de retorno void eu não consegui alterar o **HTTP Status**, o retorno era sempre **200 Ok**. Até vi uma outra abordagem, mas para isso, porém, eu precisaria ajustar a forma como o gerador de código funciona com o [Mustache](https://mustache.github.io/) para a adição do parâmetro **HttpServletResponse response** nos métodos gerados como mostrado por [Pierre Henry](https://stackoverflow.com/users/315677/pierre-henry) nessa dúvida do Stack Overflow.

```java
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){

    User newUser = ...

    //set HTTP code to "201 Created"
    response.setStatus(HttpServletResponse.SC_CREATED);
    try {
        response.flushBuffer();
    }catch(Exception e){}

    return newUser;
}
```

{% embed https://stackoverflow.com/questions/4687271/jax-rs-how-to-return-json-and-http-status-code-together %}

Por outro lado, utilizar a opção **returnResponse** como true o retorno é a classe **javax.ws.rs.core.Response**, o problema é que diferente da solução do **Spring** o **Response** do **JAX-RS** não utiliza generics. Observe a diferença do **JAX-RS** e do **Spring** para o mesmo problema:

```java
import javax.ws.rs.core.Response;

public interface RestaurantesApi {

    @PUT
    @Path("/{idRestaurante}/pratos/{idPrato}")
    @Consumes({ "application/json" })
    Response atualizaPratoRestaurante(
        @PathParam("idPrato") Long idPrato,
        @PathParam("idRestaurante") Long idRestaurante,
        @Valid AtualizacaoPrato atualizacaoPrato
    );

    @GET
    @Path("/{idRestaurante}/pratos")
    @Produces({ "application/json" })
    Response recuperaPratosRestaurante(@PathParam("idRestaurante") Long idRestaurante);
}
```

```java

import org.springframework.http.ResponseEntity;

public interface RestaurantesApi {

    default ResponseEntity<Void> atualizaPratoRestaurante(
        @Parameter(name = "idPrato", description = "", required = true) @PathVariable("idPrato") Long idPrato,
        @Parameter(name = "idRestaurante", description = "", required = true) @PathVariable("idRestaurante") Long idRestaurante,
        @Parameter(name = "AtualizacaoPrato", description = "") @Valid @RequestBody(required = false) AtualizacaoPrato atualizacaoPrato
    ) {
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }

    default ResponseEntity<List<Prato>> recuperaPratosRestaurante(
        @Parameter(name = "idRestaurante", description = "", required = true) @PathVariable("idRestaurante") Long idRestaurante
    ) {
        getRequest().ifPresent(request -> {
            for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                    String exampleString = "{ \"preco\" : 6.027456183070403, \"nome\" : \"nome\", \"id\" : 0, \"descricao\" : \"descricao\" }";
                    ApiUtil.setExampleResponse(request, "application/json", exampleString);
                    break;
                }
            }
        });
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }
}
```

Observe que **ResponseEntity** do **Spring** utiliza generics e **Response** do **JAX-RS** não. Com isso, acho a solução do Spring para o problema muito mais elegante, pois garanto o tipo de entidade do retorno, diferente da opção **Response** em que poderíamos alterar o objeto de retorno sem que houvesse um erro de compilação.

O código utilizado como referência se encontra em **applications/cadastro/src/main/java/com/gitlab/arthurfnsc/ifood/cadastro/RestauranteResource**:

```java
    @Override
    @Counted(name = "Quantidade buscas restaurante")
    @SimplyTimed(name = "Tempo simples de busca")
    @Timed(name = "Tempo completo de busca")
    public Response recuperaPratosRestaurante(Long idRestaurante) {
        Optional<Restaurante> restauranteOp = Restaurante.findByIdOptional(
            idRestaurante
        );
        if (restauranteOp.isEmpty()) {
            throw new NotFoundException("Restaurante não existe");
        }
        List<Prato> pratos = Prato.list("restaurante", restauranteOp.get());
        return Response.ok(pratoMapper.paraListaPratoApi(pratos)).build();
    }
```

No exemplo acima, poderíamos colocar qualquer coisa dentro de **Response.ok()**, porém, isso não geraria erro de compilação:

```java
    @Override
    @Counted(name = "Quantidade buscas restaurante")
    @SimplyTimed(name = "Tempo simples de busca")
    @Timed(name = "Tempo completo de busca")
    public Response recuperaPratosRestaurante(Long idRestaurante) {
        return Response.ok(LocalDate.now()).build();
    }
```

**Lembrando que analisando friamente esse é um problema decorrente da minha escolha arquitetural. O Vinícius não teve esse problema na resolução dele.**

### Mais "problemas" com OpenAPI e JAX-RS

Outro problema que encontrei foi com anotações do **Swagger**.

Observe uma classe gerada em um projeto **SpringBoot**:

```java
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

public interface RestaurantesApi {

    @Operation(
        operationId = "atualizaPratoRestaurante",
        tags = { "prato", "restaurante" },
        responses = {
            @ApiResponse(responseCode = "204", description = "No Content")
        },
        security = {
            @SecurityRequirement(name = "ifood_auth", scopes={ "write:restaurantes" })
        }
    )
    @RequestMapping(
        method = RequestMethod.PUT,
        value = "/restaurantes/{idRestaurante}/pratos/{idPrato}",
        consumes = { "application/json" }
    )
    default ResponseEntity<Void> atualizaPratoRestaurante(
        @Parameter(name = "idPrato", description = "", required = true) @PathVariable("idPrato") Long idPrato,
        @Parameter(name = "idRestaurante", description = "", required = true) @PathVariable("idRestaurante") Long idRestaurante,
        @Parameter(name = "AtualizacaoPrato", description = "") @Valid @RequestBody(required = false) AtualizacaoPrato atualizacaoPrato
    ) {
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }
}
```

Nessa abordagem vemos que é documentado o Request e Response do nosso método em nossa interface. Temos um "problema" com o **JAX-RS**, o **Swagger** não faz parte da especificação do **Java**, e precisaríamos utilizar o **Microprofile** para isso.

{% embed https://github.com/quarkusio/quarkus/issues/22429 %}

> "Yes, we are based on microprofile open API and does not support swagger. You can remove the swagger dependency from your classpath",  [phillip-kruger](https://github.com/phillip-kruger)

Até vi a [issue 795 aberta no OpenAPIGen](https://github.com/OpenAPITools/openapi-generator/issues/795).

Além de duas abordagens, uma com o [Apache Geronimo](https://github.com/apache/geronimo-openapi):

{% embed https://stackoverflow.com/questions/58317084/microprofile-generate-open-api-file-on-build %}

E outra com o **MicroGen**:

{% embed https://microgen.io/ %}

Mas não cheguei a testar nenhuma das duas.

Na abordagem que estamos seguindo, para minha filosofia a escrita de **org.eclipse.microprofile.openapi.annotations.responses.APIResponseSchema** ou **org.eclipse.microprofile.openapi.annotations.tags.Tag** na nossa classe de **Resource** configura uma intervenção manual que pode levar a algum problema futuro se eu esquecer de evoluir esse métodos. Por isso, **eu optei por não realizar tal configuração**.

Por esse motivo também desabilitei a configuração **useSwaggerAnnotations**, uma vez que não são as anotações que desejamos expor.

**Lembrando que com relação os problemas que eu levantei estão atrelados a decisões arquiteturais que eu decidi seguir. O Response do JAX-RS é um que acho que poderia ser melhorado, mas os demais estão mais atrelados à minha escolha pelo OpenAPI Generation**.

## Demais configurações

As próximas configurações do plugin dizem respeito ao vínculo da geração de código à task de compilação de Java, bem como colocar as classes geradas no nosso **sourceSet**.

```groovy
compileJava.dependsOn(
    generateApiServer
)

sourceSets.main.java.srcDir "$apiServerOutput/src/gen/java"
```

Uma vez que essas configurações estejam definidas basta executar a task **generateApiServer** ou mesmo a task **compileJava** ou mesmo alguma que dependa dela, como **build**, **test**.

Com isso, nossas classes serão geradas em **"$buildDir/generated/openapi-code-server".toString()** com base no **OpenAPI** definido em **applications/cadastro/src/main/resources/openapi/restaurantes_v1.yml**.

Na nossa classe de **Resource** **applications/cadastro/src/main/java/com/gitlab/arthurfnsc/ifood/cadastro/RestauranteResource** implentamos a interface gerada:

```java
import org.openapi.server.v1.restaurantes.api.RestaurantesApi;

public class RestauranteResource implements RestaurantesApi {

}
```

Isso já será o suficiente para recebermos um erro de compilação, pois precisamos implementar os métodos da interface que estamos implementando.

Essa é uma das vantagens do conceito de **API First**! Imagine que adicionemos outros métodos em nossa **OpenAPI** ou que removamos um método ou que adicionemos mais parâmetros em um método. Quando executarmos a task de geração de código receberemos erro de compilação, seja por métodos que removemos, seja por métodos que precisamos adicionar ou adicionar parâmetros.

---

## Conclusão e observações da decisão arquitetural

{% embed https://giphy.com/gifs/abcnetwork-decision-decide-kevin-oleary-L3c8aAACw7Qpx2ojGB %}

A escolha arquitetural da utilização de **API First** com **Open API Generator** nos trouxe alguns benefícios como:

* Acoplamento com o contrato de serviço e suas evoluções.
* Facilidade na descoberta de quebra de contrato de APIs definidas.

Porém, também nos trouxe alguns desafios que não foram passados pelo @viniciusfcf dado o fato de outra abordagem arquitetural:

* Liberdade de implementação de endpoints
* Gerador do **JAX-RS** não estar 100% alinhado com nossas expectativas.

Para além disso, gostaria de trazer mais alguns pontos que já testei com a abordagem e coisas que percebi que ela não resolve:

## OpenAPI Generator sempre vai estar defasado com relação à especificação

Um ponto interessante para gerar uma discussão sobre o **OpenAPI Generator** é que ele invariavelmente vai estar defasado com relação ao número de features da própria especificação **OpenAPI**, afinal, quando uma abordagem como essa é criada, o foco inicial é na resolução dos problemas mais comuns e não de todos os problemas.

Pegue por exemplo uma coisa bem bacana do **OpenAPI 3**, a [reutilização de responses](https://swagger.io/docs/specification/describing-responses/) com **component responses**. O código a seguir está na seção **Reusing Responses**:

```yml
/users:
    get:
      summary: Gets a list of users.
      response:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArrayOfUsers'
        '401':
          $ref: '#/components/responses/Unauthorized'   # <-----
  /users/{id}:
    get:
      summary: Gets a user by ID.
      response:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'   # <-----
        '404':
          $ref: '#/components/responses/NotFound'       # <-----
# Descriptions of common components
components:
  responses:
    NotFound:
      description: The specified resource was not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    # Schema for error response body
    Error:
      type: object
      properties:
        code:
          type: string
        message:
          type: string
      required:
        - code
        - message
```

Veja que dessa forma podemos reusar as seguintes especificações:

```yml
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
```

Isso tornaria nossa especificação mais enxuta. Há alguns anos atrás quando testei isso não funcionava com o gerador. Testando hoje, vi que funciona, porém, é bem comum ver pontos de melhoria e adequação solicitados nas [issues do projeto](https://github.com/OpenAPITools/openapi-generator/issues).

Invariavelmente ficamos atrelados ao gerador para o bem ou para o mal.

## OpenAPI Generator definitivamente não combina com Serverless

Se você clonou o projeto e importou em sua IDE, provavelmente percebeu vários erros decorrentes das classes que precisam ser geradas para que sejam encontradas. Por isso colocamos o seguinte código na nossa configuração.

```groovy
compileJava.dependsOn(
    generateApiServer
)
```

Se por um acaso você leu o README e foi executar o projeto, percebeu o seguinte output:

```sh
./gradlew clean :applications:cadastro:quarkusDev
################################################################################
# Thanks for using OpenAPI Generator.                                          #
# Please consider donation to help us maintain this project 🙏                 #
# https://opencollective.com/openapi_generator/donate                          #
################################################################################
Successfully generated code to /home/arthurfnsc/repos/udemy/ifood/applications/cadastro/build/generated/openapi-code-server
Listening for transport dt_socket at address: 5005
```

Note que para executar o projeto do zero precisamos criar algumas classes: **Successfully generated code to /home/arthurfnsc/repos/udemy/ifood/applications/cadastro/build/generated/openapi-code-server**.

![AWS Lambda Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4gpflx0oj9j3z6vqknx.png)

No exemplo acima, imagine que o nosso projeto **AWS Lambda Function** utilize essa estratégia. Iremos perder um tempo para executar a requisição porque vamos precisar gerar código antes. E, mesmo depois disso, se nossa Lambda entrar em um contexto de inativação, será necessário gerar novamente código, onerando o processo.

No demais, lembrem-se, não existe bala de prata!

No próximo post falaremos da abordagem de **API First** em projetos de **APIs reativas**

---

<a name="serie_completa"></a>

Esse post faz parte de uma série sobre **Cursos que formaram meu caráter: Desenvolvimento web com Quarkus**.

A série completa é:

* [Talk is cheap show me the code](https://dev.to/arthurfnsc/cursos-que-formaram-meu-carater-desenvolvimento-web-com-quarkus-talk-is-cheap-show-me-the-code-2k91)
* [Utilizando Gradle ao invés de Maven](https://dev.to/arthurfnsc/cursos-que-formaram-meu-carater-utilizando-gradle-ao-inves-de-maven-1lcn)
* [Plugins do Gradle: Gerenciador de versões de bibliotecas com Versions](https://dev.to/arthurfnsc/cursos-que-formaram-meu-carater-desenvolvimento-web-com-quarkus-gerenciador-de-versoes-de-bibliotecas-com-versions-9na)
* [Plugins do Gradle: Saudades do  Maven, relatórios com Project-report](https://dev.to/arthurfnsc/cursos-que-formaram-meu-carater-desenvolvimento-web-com-quarkus-saudades-do-maven-relatorios-com-project-report-51d6)
* Plugins do Gradle: API First com o OpenAPI Generator
* [Plugins do Gradle: API First com o OpenAPI Generator para APIs reativas](https://dev.to/arthurfnsc/cursos-que-formaram-meu-carater-desenvolvimento-web-com-quarkus-api-first-com-o-openapi-generator-para-apis-reativas-3o9b)
* [Plugins do Gradle: Validação de vulnerabilidades de dependências com OWASP Dependency Check](https://dev.to/arthurfnsc/cursos-que-formaram-meu-carater-desenvolvimento-web-com-quarkus-validacao-de-vulnerabilidades-com-owasp-dependency-check-2a2l)
* Plugins do Gradle: Lint com Spotless [não publicado]
* GitLab: Motivação para a escolha [não publicado]
* GitLab: Considerações sobre o Prettier em pipelines [não publicado]
* Gitmoji [não publicado]
* SDK Man [não publicado]
* Pré commit hook [não publicado]
* application.yml: Utilizando YML em propriedades de aplicação [não publicado]
* application.yml: Utilizando variáveis de ambiente [não publicado]
* application.yml: Sobrescrevendo opções em teste [não publicado]
* Mapstruct: Utilizando expressões para mapeamentos [não publicado]
* Microprofile SecurityScheme: Sobrescrevendo tokenUrl [não publicado]
* Prometheus: O problema simples que me custou algumas horas [não publicado]
* quarkus-hibernate-reactive-panache: Utilizando as vantagens do Hibernate em projetos reativos [não publicado]
* Testcontainers: Configuração para projetos reativos [não publicado]