In a previous post, I explained how to generate the OpenAPI document during the ASP.NET Core build and commit it to the repository. That gives you a versioned API contract that is easy to review in pull requests.
In this post, let's go one step further: use that generated OpenAPI file as the input for Kiota, so your typed .NET client is also generated during build.
The goal is simple:
- The server produces the OpenAPI document at build time.
- The client project references that OpenAPI file.
- Kiota runs during build only when the OpenAPI file changes.
Building the solution should then produce an updated OpenAPI file and regenerate the client if needed, all in one go.
#Generate the OpenAPI file in the server project
In the Web API project, configure build-time OpenAPI generation:
XML
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<OpenApiGenerateDocumentsOnBuild>true</OpenApiGenerateDocumentsOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.8" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
After dotnet build, the OpenAPI document is generated next to the project (for example WebApi.json). Commit this file so it is tracked and reviewed.
#Reference the OpenAPI file from the client project
In the client project, reference the server project only to enforce build order, and define the OpenAPI file path:
XML
<ItemGroup>
<ProjectReference Include="..\WebApi\WebApi.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>
<PropertyGroup>
<ApiSpecFile>$(MSBuildThisFileDirectory)..\WebApi\WebApi.json</ApiSpecFile>
<KiotaOutputDir>$(MSBuildThisFileDirectory)Generated</KiotaOutputDir>
<KiotaStampFile>$(IntermediateOutputPath)kiota.stamp</KiotaStampFile>
</PropertyGroup>
ReferenceOutputAssembly="false" ensures the API project builds first, but the client assembly does not directly reference the server assembly.
#Run Kiota from MSBuild
Install Kiota as a local .NET tool (recommended so builds do not depend on a global installation):
Shell
dotnet new tool-manifest
dotnet tool install Microsoft.OpenApi.Kiota
Then run Kiota from custom MSBuild targets:
XML
<Target Name="RestoreKiotaTool" BeforeTargets="GenerateKiotaClient">
<Exec Command="dotnet tool restore" />
</Target>
<Target Name="GenerateKiotaClient"
BeforeTargets="BeforeCompile"
DependsOnTargets="RestoreKiotaTool"
Inputs="$(ApiSpecFile)"
Outputs="$(KiotaStampFile)">
<Exec Command='dotnet kiota generate -l CSharp -d "$(ApiSpecFile)" -c ApiClient -n MyCompany.MyApi.Client -o "$(KiotaOutputDir)" --clean-output' />
<Touch Files="$(KiotaStampFile)" AlwaysCreate="true" />
</Target>
<ItemGroup>
<Compile Include="Generated\**\*.cs" />
</ItemGroup>
The important part is Inputs and Outputs on GenerateKiotaClient:
- If
$(ApiSpecFile) did not change since the last build, MSBuild skips the target. - If the OpenAPI file changed, Kiota regenerates the client.
This keeps builds fast while still ensuring the client stays aligned with the current contract.
#Required Kiota runtime dependencies
The generated code needs Kiota runtime packages. For .NET, the simplest option is:
Shell
dotnet add package Microsoft.Kiota.Bundle
Depending on your authentication scenario, you may also need packages such as Microsoft.Kiota.Authentication.Azure and Azure.Identity.
#Typical developer workflow
With this setup, the workflow is straightforward:
- Change an endpoint or model in the server.
- Run
dotnet build. - Review changes in both
WebApi.json and generated client files. - Commit everything together.
This makes contract evolution explicit and safe. If an API change is accidental, you usually notice it immediately in the OpenAPI diff or in generated client changes.
#Additional resources
Do you have a question or a suggestion about this post? Contact me!