Gerando um cliente Kiota em tempo de build no ASP.NET Core
Gerar o cliente Kiota automaticamente durante o build é a forma mais segura de manter o consumo de uma API ASP.NET Core sincronizado com o contrato publicado. Neste artigo, você vai combinar a geração do documento OpenAPI em tempo de build com um target MSBuild que invoca o Kiota apenas quando a especificação muda.
Por que automatizar o cliente Kiota
A integração entre serviços em aplicações .NET exige um processo eficiente para criar e manter clientes HTTP.
Manter clientes escritos à mão, por outro lado, é trabalhoso e propenso a erros, principalmente quando a API de origem evolui com frequência.
Cada novo endpoint, campo ou código de status precisa ser replicado manualmente no consumidor. Por isso, qualquer esquecimento vira um bug em produção.
O Kiota resolve esse problema porque gera clientes fortemente tipados a partir de documentos OpenAPI.
Quando o cliente Kiota nasce do mesmo build que produz a especificação, o contrato e o consumidor deixam de divergir. Em outras palavras, a fonte da verdade passa a ser o próprio código da API.
O que é o Kiota
O Kiota é uma ferramenta de linha de comando de código aberto, mantida pela Microsoft, que gera clientes de API a partir de documentos OpenAPI.
Ao contrário de abordagens que exigem uma biblioteca diferente para cada API consumida, o Kiota padroniza a experiência com um conjunto de pacotes base reutilizáveis.
O cliente gerado oferece tipagem forte, autocomplete na IDE e acesso completo aos recursos do protocolo HTTP. Além disso, a ferramenta produz apenas o código necessário para a superfície de API selecionada.
O Kiota também suporta linguagens como C#, TypeScript, Go, Java, Python, PHP e Ruby. Assim, ele se torna uma escolha versátil em ambientes poliglotas.
No .NET, todo cliente Kiota depende do pacote Microsoft.Kiota.Bundle, que agrupa abstrações, serialização e adaptadores de transporte HTTP.
Se você ainda não usou a ferramenta, vale ler antes o artigo como usar o Kiota para gerar SDKs de API no .NET, que cobre os comandos básicos.
Geração do documento OpenAPI em tempo de build
A partir do .NET 9, o ASP.NET Core inclui suporte nativo à geração de documentos OpenAPI por meio do pacote Microsoft.AspNetCore.OpenApi.
Por padrão, a aplicação gera esse documento em tempo de execução e o expõe por um endpoint HTTP, como /openapi/v1.json.
Em alguns cenários, porém, o arquivo precisa existir em disco. Pipelines de CI/CD, testes baseados em contrato e geradores de cliente são exemplos típicos.
Nesse caso, o pacote Microsoft.Extensions.ApiDescription.Server permite emitir o arquivo durante o dotnet build. A instalação é simples:
dotnet add package Microsoft.AspNetCore.OpenApi
dotnet add package Microsoft.Extensions.ApiDescription.Server
Com os dois pacotes referenciados, o build da API emite o arquivo OpenAPI automaticamente no diretório de saída do projeto.
Para personalizar o destino, configure a propriedade OpenApiDocumentsDirectory no .csproj da API:
<PropertyGroup>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)/../MyClient/OpenApi</OpenApiDocumentsDirectory>
</PropertyGroup>
Essa configuração posiciona o arquivo diretamente no projeto cliente. Dessa forma, o consumidor encontra o artefato pronto no momento do próprio build.
Segundo a documentação oficial de geração de documentos OpenAPI, o valor de OpenApiDocumentsDirectory é relativo ao arquivo de projeto.
A mesma documentação explica que a geração em build executa o ponto de entrada da aplicação com um servidor simulado. Portanto, códigos de inicialização que acessam banco ou configurações sensíveis podem precisar de uma condição extra.
O artigo OpenAPI no ASP.NET Core em tempo de build detalha essas opções com mais profundidade.
O fim do Microsoft.Extensions.ApiDescription.Client
Vale observar que, a partir do .NET 10, a Microsoft descontinuou o pacote Microsoft.Extensions.ApiDescription.Client.
Esse pacote fornecia o item OpenApiReference e o comando dotnet openapi, que geravam clientes durante o build. Agora, projetos que ainda o referenciam recebem avisos de compilação.
A recomendação oficial é migrar para a ferramenta de cada gerador, como o próprio Kiota. Com isso, desaparece a camada intermediária que o pacote oferecia.
Estrutura da solução com API e cliente Kiota
Antes de configurar qualquer target, vale visualizar como os projetos se organizam na solução.
O exemplo deste artigo usa dois projetos lado a lado: a API ASP.NET Core e uma biblioteca chamada MyClient, que hospeda o cliente Kiota.
MySolution/
├── MyApi/
│ └── MyApi.csproj
└── MyClient/
├── MyClient.csproj
├── OpenApi/
│ ├── myapi.json
│ └── myapi.json.hash
└── Generated/
A pasta OpenApi recebe a especificação emitida pelo build da API. Já a pasta Generated guarda apenas o código que o Kiota produz.
Essa separação deixa claro o que é contrato, o que é código gerado e o que é código escrito pela equipe.
Além disso, ela permite que outros consumidores reaproveitem a mesma biblioteca. Basta referenciar o projeto MyClient em vez de gerar um novo cliente em cada aplicação.
Instalação do Kiota como ferramenta local
Para integrar o Kiota ao pipeline de build, a abordagem recomendada é instalá-lo como ferramenta .NET local. Assim, a versão fica registrada no arquivo .config/dotnet-tools.json, junto ao repositório.
Caso o manifesto de ferramentas ainda não exista, crie-o com o comando abaixo:
dotnet new tool-manifest
Em seguida, adicione o Kiota ao manifesto:
dotnet tool install Microsoft.OpenApi.Kiota
A instalação local garante que toda a equipe e os agentes de CI usem exatamente a mesma versão da ferramenta.
Depois de um dotnet tool restore, o comando dotnet kiota fica disponível sem nenhuma instalação global na máquina.
Essa escolha também evita diferenças sutis no código gerado. Afinal, versões distintas do gerador podem produzir arquivos ligeiramente diferentes e poluir os pull requests.
Configuração do projeto do cliente Kiota
O projeto que vai consumir a API precisa referenciar o pacote de runtime do Kiota:
dotnet add package Microsoft.Kiota.Bundle
Com a dependência instalada, o próximo passo é ensinar o .csproj a invocar o Kiota durante o build.
Para isso, o MSBuild oferece o elemento <Target> com o atributo AfterTargets, que executa ações depois de etapas específicas da compilação.
A estrutura básica de um target que gera o cliente Kiota fica assim:
<Target Name="GenerateKiotaClient" AfterTargets="Build">
<Exec
Command="dotnet kiota generate -l CSharp -c MyApiClient -n MyApp.Client -d ./OpenApi/myapi.json -o ./Generated --clean-output --exclude-backward-compatible"
WorkingDirectory="$(ProjectDir)" />
</Target>
Esse target executa o Kiota depois de cada build bem-sucedido e regenera todo o código do cliente.
Parâmetros que definem o cliente Kiota
O parâmetro -l define a linguagem de saída, enquanto -c e -n escolhem o nome da classe raiz e o namespace.
O parâmetro -d aponta para o arquivo OpenAPI que o projeto da API produziu.
Já o parâmetro -o define o diretório de saída do código gerado.
A flag --clean-output remove arquivos obsoletos do diretório antes de gerar os novos. Dessa forma, endpoints removidos da API também desaparecem do cliente.
Por fim, a flag --exclude-backward-compatible reduz o tamanho do cliente. Ela remove sobrecargas de compatibilidade retroativa que raramente fazem falta em projetos internos.
Detecção de mudanças via hash no cliente Kiota
Executar o Kiota a cada build custa tempo, principalmente quando a especificação OpenAPI continua igual.
A solução é comparar o hash atual do arquivo OpenAPI com um hash salvo em disco. Então, o target só invoca a geração quando os valores divergem.
O MSBuild oferece a task <FileHash> para calcular checksums de arquivos. Além disso, o atributo Condition controla quando cada task roda.
A abordagem completa segue este padrão no .csproj:
<Target Name="GenerateKiotaClient" AfterTargets="Build">
<!-- Calcula o hash atual do arquivo OpenAPI -->
<FileHash Files="$(ProjectDir)OpenApi/myapi.json" Algorithm="SHA256">
<Output TaskParameter="Hash" ItemName="CurrentHash" />
</FileHash>
<!-- Lê o hash salvo anteriormente, se existir -->
<ReadLinesFromFile File="$(ProjectDir)OpenApi/myapi.json.hash"
Condition="Exists('$(ProjectDir)OpenApi/myapi.json.hash')">
<Output TaskParameter="Lines" ItemName="SavedHashLines" />
</ReadLinesFromFile>
<PropertyGroup>
<CurrentHashValue>@(CurrentHash)</CurrentHashValue>
<SavedHashValue>@(SavedHashLines)</SavedHashValue>
</PropertyGroup>
<!-- Executa o Kiota apenas se o hash mudou -->
<Exec
Command="dotnet kiota generate -l CSharp -c MyApiClient -n MyApp.Client -d ./OpenApi/myapi.json -o ./Generated --clean-output --exclude-backward-compatible"
WorkingDirectory="$(ProjectDir)"
Condition="'$(CurrentHashValue)' != '$(SavedHashValue)'" />
<!-- Persiste o hash atual para comparação futura -->
<WriteLinesToFile
File="$(ProjectDir)OpenApi/myapi.json.hash"
Lines="$(CurrentHashValue)"
Overwrite="true"
Condition="'$(CurrentHashValue)' != '$(SavedHashValue)'" />
</Target>
Com essa configuração, o Kiota só roda quando alguém modifica de fato o documento OpenAPI.
Repare que o hash só muda depois que a geração termina. Assim, se o comando falhar, o próximo build tenta gerar o cliente Kiota novamente.
Adicione o arquivo .hash ao controle de versão. Desse modo, o pipeline de CI também aproveita a detecção incremental.
Quando o arquivo OpenAPI ainda não existe
Em um clone novo do repositório, o arquivo OpenAPI pode não existir antes do primeiro build da API.
Nesse caso, condicione o target com Condition="Exists('caminho/do/arquivo.json')". Com isso, o build do consumidor não falha enquanto a especificação ainda não foi emitida.
Registro do cliente Kiota com injeção de dependência
Com o código gerado disponível, registre o cliente Kiota no contêiner de injeção de dependência por meio de métodos de extensão.
O padrão recomendado usa IHttpClientFactory para gerenciar o ciclo de vida do HttpClient. Por isso, o tutorial oficial cria uma fábrica que recebe o HttpClient pelo construtor:
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Http.HttpClientLibrary;
using MyApp.Client;
public class MyApiClientFactory
{
private readonly IAuthenticationProvider _authenticationProvider;
private readonly HttpClient _httpClient;
public MyApiClientFactory(HttpClient httpClient)
{
// API interna sem autenticação neste exemplo
_authenticationProvider = new AnonymousAuthenticationProvider();
_httpClient = httpClient;
}
public MyApiClient CreateClient() =>
new(new HttpClientRequestAdapter(_authenticationProvider, httpClient: _httpClient));
}
Em seguida, um método de extensão concentra todo o registro:
public static class MyApiClientServiceCollectionExtensions
{
public static IServiceCollection AddMyApiClient(
this IServiceCollection services,
string baseUrl)
{
// Registra os handlers padrão do Kiota no contêiner
services.AddKiotaHandlers();
services.AddHttpClient<MyApiClientFactory>(client =>
{
client.BaseAddress = new Uri(baseUrl);
}).AttachKiotaHandlers();
services.AddTransient(sp =>
sp.GetRequiredService<MyApiClientFactory>().CreateClient());
return services;
}
}
Os métodos AddKiotaHandlers() e AttachKiotaHandlers() vêm do tutorial oficial de injeção de dependência com Kiota. Você os copia para o projeto, porque eles não fazem parte do pacote.
Eles adicionam os middlewares do Kiota ao pipeline do HttpClient, como retry, redirecionamento e tratamento de erros.
O registro em Program.cs fica com uma única linha:
builder.Services.AddMyApiClient("https://my-api.example.com");
A partir desse ponto, o MyApiClient fica disponível para injeção em qualquer serviço, controller ou handler da aplicação.
Fluxo completo de trabalho
O fluxo resultante é totalmente automatizado e se comporta da seguinte forma a cada dotnet build.
Primeiro, o projeto da API gera o documento OpenAPI atualizado com Microsoft.Extensions.ApiDescription.Server e o grava no diretório configurado no projeto cliente.
Depois, o projeto cliente compara o hash do arquivo recebido com o hash persistido da última geração.
Caso haja divergência, o Kiota regenera o cliente e o target salva o novo hash.
Em builds seguintes sem alteração na API, o target detecta hashes iguais e ignora o Kiota. Consequentemente, o tempo total de compilação cai.
Esse modelo é especialmente valioso em monorepos, onde API e cliente convivem no mesmo repositório. Afinal, qualquer mudança de contrato chega ao consumidor sem intervenção manual.
Para garantir a ordem correta, o projeto cliente pode referenciar o projeto da API com ReferenceOutputAssembly="false". Assim, o MSBuild compila a API antes, mas não acopla os assemblies.
Adicionando o target de limpeza
Para manter a consistência ao executar dotnet clean, vale adicionar um target que remova os arquivos gerados:
<Target Name="CleanKiotaClient" AfterTargets="CoreClean">
<RemoveDir Directories="$(ProjectDir)Generated" />
<Delete Files="$(ProjectDir)OpenApi/myapi.json.hash" />
</Target>
Esse target descarta tanto o código gerado quanto o hash persistido. Portanto, o próximo build força uma regeneração limpa do cliente Kiota.
Versionar ou não o cliente Kiota gerado
Uma dúvida recorrente é se o código gerado pelo Kiota deve entrar no repositório.
Commitar os arquivos gerados dá visibilidade imediata às mudanças de contrato nos pull requests. Além disso, facilita a revisão e dispensa a geração em pipelines que não executam dotnet build.
Não commitar, por outro lado, simplifica o repositório. No entanto, o CI passa a depender sempre do build completo antes de qualquer etapa que use o cliente.
A própria nota de descontinuação da Microsoft sugere commitar o código gerado ou rodar a geração em uma etapa própria de build.
Para projetos internos em monorepo, a recomendação é commitar o diretório Generated e tratá-lo como um artefato versionado e rastreável.
Boas práticas
Nunca escreva código manual dentro do diretório de saída do Kiota, porque a próxima geração vai sobrescrever qualquer modificação.
Extensões, provedores de autenticação e registros de DI, portanto, ficam fora da pasta Generated.
O arquivo de especificação versionado funciona como contrato formal entre produtor e consumidor. Por isso, ele precisa refletir fielmente o estado atual da API.
Em ambientes com vários consumidores, publique o arquivo OpenAPI como artefato de CI. Assim, projetos externos geram o próprio cliente Kiota sem acesso ao código-fonte da API.
Para APIs com autenticação, troque o AnonymousAuthenticationProvider por um provedor adequado. O pacote Microsoft.Kiota.Authentication.Azure, por exemplo, integra o cliente ao Azure Identity.
Se a geração falhar, use --log-level debug no comando. Também ajuda definir ConsoleToMSBuild="true" na task <Exec>, que envia a saída para o log do build.
Conclusão
A combinação da geração de OpenAPI em build do ASP.NET Core com targets MSBuild do Kiota cria um pipeline de integração totalmente automatizado.
A detecção de mudanças via hash elimina regenerações desnecessárias. Dessa forma, o processo continua rápido mesmo em projetos grandes.
O resultado é um fluxo em que toda alteração de contrato chega ao cliente Kiota sozinha, com rastreabilidade no controle de versão e sem etapas manuais.
Se quiser comparar com outras formas de documentar e expor a especificação, veja também o artigo sobre documentação de APIs com OpenAPI, Swagger e Scalar.
FAQ
O que acontece se o arquivo OpenAPI não existir durante o build do cliente?
O target pode receber a condição Exists('caminho/do/arquivo.json') para não falhar enquanto o arquivo ainda não foi gerado.
É possível usar o Kiota com APIs externas fora do repositório?
Sim. O parâmetro -d do kiota generate aceita tanto caminhos locais quanto URLs de especificações públicas ou internas.
O Kiota suporta OAuth2 e Microsoft Entra ID?
Sim. O pacote Microsoft.Kiota.Authentication.Azure oferece um provedor baseado no Azure Identity, compatível com os fluxos OAuth2 do Entra ID.
Por que o Microsoft.Extensions.ApiDescription.Client foi descontinuado no .NET 10?
O pacote tinha pouca manutenção e abstrações muito acopladas a geradores específicos. Por isso, a Microsoft delegou a cada gerador o próprio fluxo de configuração.
O código gerado pelo Kiota é compatível com Native AOT?
O suporte a Native AOT no Kiota ainda evolui. Para esse cenário, confira a documentação oficial da versão em uso, porque a compatibilidade varia conforme os recursos utilizados.
Como depurar a geração do cliente em caso de falha?
Use --log-level debug no kiota generate e ConsoleToMSBuild="true" na task <Exec> para ver a saída detalhada no log do build.