A Roslyn Source Generator that automatically generates REST API server and client code from OpenAPI specifications. No CLI commands, no manual code generation - just build your project and get production-ready code.
| Area | Feature | Description |
|---|---|---|
| Core | π§ Zero Configuration | Add the NuGet package, drop your YAML file, build |
| ποΈ Minimal API | Generates modern ASP.NET Core minimal API endpoints | |
| π Contract-Enforced Results | Handlers can only return responses defined in OpenAPI - compile-time safety! | |
| β Validation | Automatic [Required], [Range], [StringLength] from OpenAPI constraints |
|
| Server | π Handler Scaffolds | Auto-generates handler stubs for unimplemented operations |
| π Security | JWT, OAuth2 scopes, API Key, role-based auth - all from OpenAPI | |
| β±οΈ Rate Limiting | Server-side rate limiting from x-ratelimit-* extensions |
|
| πΎ Caching | Output caching and HybridCache support from x-cache-* extensions |
|
| Client | π Type-Safe Client | Strongly-typed HTTP client with full IntelliSense |
| π Resilience | Client-side retry/circuit breaker from x-retry-* extensions |
|
| π URL Encoding | Automatic RFC 3986 compliant encoding - no more broken URLs | |
| Models | π― Enum Support | Generates C# enums from OpenAPI string enums |
| π Pagination | Generic PaginatedResult<T> from allOf composition |
|
| Data | π File Uploads | Full support for binary uploads (single, multiple, with metadata) |
| π Streaming | IAsyncEnumerable<T> support for efficient data streaming |
|
| CLI | π₯οΈ Project Scaffolding | generate server / generate client creates complete project structure |
| π¦ TypeScript Client | generate client-typescript β Fetch/Axios, React Query hooks, Zod, interceptors |
|
| π Spec Validation | spec validate validates OpenAPI specs with strict/standard modes |
|
| π Multi-Part Specs | spec merge / spec split for large API specifications |
|
| βοΈ Options Management | options create / options validate for configuration files |
|
| π Migration | migrate validate / migrate execute to migrate from old CLI generator |
dotnet add package Atc.Rest.Api.SourceGeneratorAdd your *.yaml file and marker file to .csproj:
<ItemGroup>
<AdditionalFiles Include="MyApi.yaml" />
<AdditionalFiles Include=".atc-rest-api-server" />
</ItemGroup>For a typical three-layer architecture, use different marker files per project:
| Project | Marker File | Purpose |
|---|---|---|
| Contracts | .atc-rest-api-server |
Models, handler interfaces, endpoints, DI |
| Domain | .atc-rest-api-server-handlers |
Handler implementation scaffolds |
| Client | .atc-rest-api-client |
Type-safe HTTP client |
Contracts project - .atc-rest-api-server:
{
"generate": true,
"validateSpecificationStrategy": "Standard"
}Domain project - .atc-rest-api-server-handlers:
{
"generate": true,
"generateHandlersOutput": "Handlers"
}π‘ See Marker Files Reference for all configuration options.
dotnet buildGenerated code includes:
- π¦ Models - C# records with validation attributes
- π Handler Interfaces -
IListPetsHandler,ICreatePetHandler, etc. - π£οΈ Endpoints - Minimal API
MapGet,MapPost, etc. - π§ DI Extensions -
AddApiHandlersFromDomain(),MapApiEndpoints()
public class GetPetByIdHandler : IGetPetByIdHandler
{
public async Task<GetPetByIdResult> ExecuteAsync(
GetPetByIdParameters parameters,
CancellationToken ct)
{
var pet = await repository.GetByIdAsync(parameters.PetId, ct);
// β
Clean syntax with implicit operator
if (pet is not null)
return pet; // Implicitly converts to GetPetByIdResult.Ok(pet)
// β
Factory methods for error responses
return GetPetByIdResult.NotFound();
// β COMPILE ERROR - Method doesn't exist!
// return GetPetByIdResult.InternalServerError();
}
}π‘ Type-Safe Results: Result classes have private constructors and factory methods matching your OpenAPI responses. You literally cannot return a status code that isn't in your spec!
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddApiHandlersFromDomain();
var app = builder.Build();
app.MapOpenApi();
app.MapApiEndpoints();
app.Run();- .NET 10 or later
- OpenAPI 3.0.x or 3.1.x specification
For EndpointPerOperation client generation mode:
<PackageReference Include="Atc" Version="3.*" />
<PackageReference Include="Atc.Rest.Client" Version="2.*" />The project uses a three-layer architecture:
| Layer | Project | Description |
|---|---|---|
| Shared | Atc.Rest.Api.Generator |
All 12 extractors, services, configuration, validation (Roslyn-independent) |
| Generators | Atc.Rest.Api.SourceGenerator |
Roslyn source generators (thin wrappers calling shared services) |
| CLI | Atc.Rest.Api.Generator.Cli |
Command-line tool for validation and generation |
Benefits: Testability, reusability across CLI and source generators, clear separation of concerns.
The atc-rest-api-gen CLI provides a guided experience for project setup and validation:
# Generate server project (contracts + domain)
atc-rest-api-gen generate server -s api.yaml -o output/MyApp.Api.Contracts -n MyApp.Api.Contracts
# Generate client project
atc-rest-api-gen generate client -s api.yaml -o output/MyApp.Client -n MyApp.Client
# Generate TypeScript client (fetch or Axios, with optional hooks/Zod/scaffold)
atc-rest-api-gen generate client-typescript -s api.yaml -o ./src/api --hooks ReactQuery
# Validate OpenAPI specification
atc-rest-api-gen spec validate -s api.yaml
# Create default configuration file
atc-rest-api-gen options create -o ./See Working with the CLI for full documentation.
Read the full documentation on the WIKI
| Document | Description |
|---|---|
| π Getting Started | Detailed setup guide with examples |
| π₯οΈ Getting Started with CLI | Quick start guide using CLI scaffolding |
| π Migration Guide | Migrate from old atc-rest-api-generator |
| βοΈ Working with the CLI | Full CLI command reference |
| π Working with OpenAPI | YAML patterns and generated output |
| π Working with Security | JWT, OAuth2, API Key authentication |
| β Working with Validations | Request validation from OpenAPI constraints |
| β±οΈ Working with Rate Limiting | Server-side rate limiting configuration |
| π Working with Resilience | Client-side retry/circuit breaker patterns |
| πΎ Working with Caching | Output caching and HybridCache configuration |
| π’ Working with Versioning | API versioning strategies |
| π Analyzer Rules | OpenAPI validation rules reference |
| π¦ Working with TypeScript Client | TypeScript/React client generation |
| πͺ Showcase Demo | Full-featured demo with Blazor & React UI |
| β FAQ & Troubleshooting | Common issues and solutions |
| πΊοΈ Roadmap | Planned features and status |
| π§ Development Notes | For contributors |
paths:
/pets:
get:
operationId: listPets
parameters:
- name: limit
in: query
schema:
type: integer
maximum: 100
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'// Generated model
public record Pet(
[property: Required] long Id,
[property: Required] string Name,
string? Tag);
// Generated parameters
public class ListPetsParameters
{
[FromQuery(Name = "limit")]
[Range(long.MinValue, 100)]
public int? Limit { get; set; }
}
// Generated handler interface
public interface IListPetsHandler
{
Task<ListPetsResult> ExecuteAsync(
ListPetsParameters parameters,
CancellationToken ct = default);
}
// Generated result - ONLY factory methods for responses in OpenAPI!
public class ListPetsResult : IResult
{
private ListPetsResult(IResult inner) { ... } // Private!
public static ListPetsResult Ok(Pet[] response) => ...
public static implicit operator ListPetsResult(Pet[] r) => Ok(r);
// No InternalServerError(), Unauthorized(), etc. - they don't exist!
}
// Generated endpoint
endpoints.MapGet("/pets", async (
[FromServices] IListPetsHandler handler,
[AsParameters] ListPetsParameters parameters,
CancellationToken ct)
=> await handler.ExecuteAsync(parameters, ct));π·οΈ Marker Files
| File | Purpose |
|---|---|
.atc-rest-api-server |
Server code (models, endpoints, handlers) |
.atc-rest-api-server-handlers |
Handler implementation scaffolds |
.atc-rest-api-client |
HTTP client generation |