DEV Community

Cover image for Cursos que formaram meu caráter: Desenvolvimento web com Quarkus - API First com o OpenAPI Generator
Arthur Fonseca
Arthur Fonseca

Posted on • Updated on

Cursos que formaram meu caráter: Desenvolvimento web com Quarkus - API First com o OpenAPI Generator

"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 vamos precisar usar o JAX-RS como ponte para geração de nossos contratos. Como grande fã do Spring 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.

Seth Meyers GIF by Late Night with Seth Meyers

Discover & share this Seth Meyers GIF by Late Night with Seth Meyers with everyone you know. GIPHY is how you search, share, discover, and create GIFs.

favicon giphy.com

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.

Estamos nos baseando no curso Desenvolvimento web com Quarkus do @viniciusfcf.

O repositório que estamos utilizando é:


SOA so far

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

Em 2012 entrei em um instituto de pesquisas, o IBTI. 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 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

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 ou Swagger 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, 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:

apply from: "$rootDir/plugins/openapigen_cadastro.gradle"
Enter fullscreen mode Exit fullscreen mode

E no arquivo plugins/openapigen_cadastro.gradle:

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"
Enter fullscreen mode Exit fullscreen mode

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 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

Sobre as configurações utilizadas em configOptions do jaxrs-spec podemos encontrar mais detalhes no seguinte link.

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/ > 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

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, 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:

@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);

...
}
Enter fullscreen mode Exit fullscreen mode

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 para a adição do parâmetro HttpServletResponse response nos métodos gerados como mostrado por Pierre Henry nessa dúvida do Stack Overflow.

@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;
}
Enter fullscreen mode Exit fullscreen mode

I'm writing a REST web app (NetBeans 6.9, JAX-RS, TopLink Essentials) and trying to return JSON and HTTP status code. I have code ready and working that returns JSON when the HTTP GET method is called from the client. Essentially:

@Path("get/id")
@GET
@Produces("application/json")
public M_機械 getMachineToUpdate(@PathParam("id") String id) {

    //

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:

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);
}
Enter fullscreen mode Exit fullscreen mode

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);

    }
}
Enter fullscreen mode Exit fullscreen mode

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:

    @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();
    }
Enter fullscreen mode Exit fullscreen mode

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

    @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();
    }
Enter fullscreen mode Exit fullscreen mode

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:

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);

    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Quarkus, Swagger Ignore method/Class #22429

Describe the bug

Hello,

I have a Quarkus app and i am using Swagger interface for those REST API, and all my methods are visible, i want to ignore some of them to dot be visible when i am using quarkus and i have tried like below, and also added some other @ApiOperation and so one,

When i am using Swagger all my methods are visible on Swagger interface so i dont know how to ignore some of them.

@GET @Produces(MediaType.TEXT_PLAIN) @Operation(hidden = true) @Schema(hidden=true) public TemplateInstance getGetSMTest(@QueryParam("name") String name){ LOG.info("called page /getSMTest"); return getSMTest.data("name", name); }

Best Regards, Daniel Sanchi

Expected behavior

No response

Actual behavior

No response

How to Reproduce?

No response

Output of uname -a or ver

No response

Output of java -version

No response

GraalVM version (if different from Java)

No response

Quarkus version or git rev

No response

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

No response

"Yes, we are based on microprofile open API and does not support swagger. You can remove the swagger dependency from your classpath", phillip-kruger

Até vi a issue 795 aberta no OpenAPIGen.

Além de duas abordagens, uma com o Apache Geronimo:

0

prior to using Eclipse Microprofile we've generated our open-api files via the io.openapitools.swagger:swagger-maven-plugin This plugin relies on the io.swagger.core.v3:swagger-annotations maven dependency.

Eclipse Microprofile though comes with a different dependency which does not seem to be enough for the openapitools plugin, therfore the generated open-api file does not contain any…

E outra com o MicroGen:

MicroGen | An OpenAPI Spec Generator for Eclipse JakartaEE + Eclipse MicroProfile

An OpenAPI Spec Generator for Eclipse JakartaEE + Eclipse MicroProfile

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.

compileJava.dependsOn(
    generateApiServer
)

sourceSets.main.java.srcDir "$apiServerOutput/src/gen/java"
Enter fullscreen mode Exit fullscreen mode

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:

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

public class RestauranteResource implements RestaurantesApi {

}
Enter fullscreen mode Exit fullscreen mode

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

Offer Decide GIF by ABC Network

Discover & share this Offer Decide GIF by ABC Network with everyone you know. GIPHY is how you search, share, discover, and create GIFs.

favicon giphy.com

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 com component responses. O código a seguir está na seção Reusing Responses:

/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
Enter fullscreen mode Exit fullscreen mode

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

        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
Enter fullscreen mode Exit fullscreen mode

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.

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.

compileJava.dependsOn(
    generateApiServer
)
Enter fullscreen mode Exit fullscreen mode

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

./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
Enter fullscreen mode Exit fullscreen mode

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

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


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

A série completa é:

Top comments (0)