Skip to content

atc-net/atc-rest-api-source-generator

Repository files navigation

πŸš€ Atc.Rest.Api.SourceGenerator

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.

✨ Key Features

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

πŸ“¦ Quick Setup

1. Install the Package

dotnet add package Atc.Rest.Api.SourceGenerator

2. Add Your OpenAPI Spec

Add your *.yaml file and marker file to .csproj:

<ItemGroup>
  <AdditionalFiles Include="MyApi.yaml" />
  <AdditionalFiles Include=".atc-rest-api-server" />
</ItemGroup>

3. Create Marker Files

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.

4. Build and Use

dotnet build

Generated code includes:

  • πŸ“¦ Models - C# records with validation attributes
  • πŸ”Œ Handler Interfaces - IListPetsHandler, ICreatePetHandler, etc.
  • πŸ›£οΈ Endpoints - Minimal API MapGet, MapPost, etc.
  • πŸ”§ DI Extensions - AddApiHandlersFromDomain(), MapApiEndpoints()

5. Implement Handlers

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!

6. Wire Up Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddApiHandlersFromDomain();

var app = builder.Build();

app.MapOpenApi();
app.MapApiEndpoints();

app.Run();

πŸ“‹ Requirements

  • .NET 10 or later
  • OpenAPI 3.0.x or 3.1.x specification

Optional Dependencies

For EndpointPerOperation client generation mode:

<PackageReference Include="Atc" Version="3.*" />
<PackageReference Include="Atc.Rest.Client" Version="2.*" />

πŸ—οΈ Architecture

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.

πŸ–₯️ CLI Tool

Installation Guide

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.

πŸ“š WIKI - 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

🎯 Generated from This YAML

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'

⬇️ To This C# Code

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

About

OpenAPI Specification to REST API code

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors