Skip to content

Getting Started with Basic

David Kallesen edited this page Dec 22, 2025 · 1 revision

🚀 Getting Started Basic

This guide helps you build your first REST API using the Atc.Rest.Api.SourceGenerator. You'll go from OpenAPI specification to a working API in minutes!

📋 Prerequisites

Before you begin, ensure you have:

  • ✅ .NET 10 SDK installed (download)
  • ✅ An IDE (Visual Studio 2026, VS Code, or Rider)
  • ✅ Basic familiarity with C# and ASP.NET Core

📦 Optional Dependencies

Some features require additional ATC packages:

Package Required For Install
Atc String utilities, extensions dotnet add package Atc
Atc.Rest.Client EndpointPerOperation client mode dotnet add package Atc.Rest.Client
<!-- Add to .csproj if using EndpointPerOperation mode -->
<ItemGroup>
  <PackageReference Include="Atc" Version="3.*" />
  <PackageReference Include="Atc.Rest.Client" Version="2.*" />
</ItemGroup>

💡 Note: The TypedClient mode (default) does not require these packages. Only add them if using EndpointPerOperation generation mode.


🎯 What You'll Build

By the end of this guide, you'll have:

  1. 📝 An OpenAPI YAML file defining your API
  2. 🌐 A working ASP.NET Core minimal API
  3. ⚙️ Auto-generated code for models, endpoints, handlers, and more
  4. 📁 A complete project structure you can extend

🏗️ Architecture Options

Choose the setup that fits your needs:

🅰️ Option A: Single-Project Setup (Simpler)

Best for: Small APIs, prototypes, learning

MyApi/
├── MyApi.yaml              # OpenAPI specification
├── MyApi.Api/              # Everything in one project
│   ├── MyApi.Api.csproj
│   ├── Program.cs
│   ├── .atc-rest-api-server-contracts
│   └── Handlers/           # Your handler implementations

🅱️ Option B: Multi-Project Setup (Recommended)

Best for: Production APIs, larger teams, clean architecture

MyApi/
├── MyApi.yaml                      # OpenAPI specification
├── MyApi.Api/                      # ASP.NET Core host
│   ├── MyApi.Api.csproj
│   └── Program.cs
├── MyApi.Api.Contracts/            # Generated contracts
│   ├── MyApi.Api.Contracts.csproj
│   ├── .atc-rest-api-server-contracts
│   └── (generated code appears here)
├── MyApi.Api.Domain/               # Your business logic
│   ├── MyApi.Api.Domain.csproj
│   ├── .atc-rest-api-server-handlers
│   └── Handlers/                   # Handler implementations
└── MyApi.ClientApp/                # Optional HTTP client
    ├── MyApi.ClientApp.csproj
    └── .atc-rest-api-client-contracts

📝 Step 1: Create Your OpenAPI Specification

Create a file named PetStore.yaml in your solution root:

# PetStore.yaml - Your API contract
openapi: 3.0.3
info:
  title: Pet Store API
  version: 1.0.0
  description: A simple pet store API

paths:
  /pets:
    get:
      operationId: listPets          # Used for handler naming
      summary: List all pets
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 100
      responses:
        '200':
          description: A list of pets
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Pet'
    post:
      operationId: createPet
      summary: Create a new pet
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Pet'
      responses:
        '201':
          description: Pet created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'

  /pets/{petId}:
    get:
      operationId: getPetById
      summary: Get a pet by ID
      parameters:
        - name: petId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: A single pet
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
        '404':
          description: Pet not found

components:
  schemas:
    Pet:
      title: Pet                    # Required for strict mode
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
          minLength: 1
          maxLength: 100
        tag:
          type: string

💡 Tip: The operationId becomes your handler interface name. listPets -> IListPetsHandler


📁 Step 2: Create the Project Structure

🅰️ For Single-Project Setup

# Create solution and project
dotnet new sln -n PetStore
dotnet new web -n PetStore.Api
dotnet sln add PetStore.Api

# Add the source generator package
cd PetStore.Api
dotnet add package Atc.Rest.Api.SourceGenerator

🅱️ For Multi-Project Setup

# Create solution
dotnet new sln -n PetStore

# Create projects
dotnet new web -n PetStore.Api
dotnet new classlib -n PetStore.Api.Contracts
dotnet new classlib -n PetStore.Api.Domain

# Add to solution
dotnet sln add PetStore.Api
dotnet sln add PetStore.Api.Contracts
dotnet sln add PetStore.Api.Domain

# Add project references
dotnet add PetStore.Api reference PetStore.Api.Contracts
dotnet add PetStore.Api reference PetStore.Api.Domain
dotnet add PetStore.Api.Domain reference PetStore.Api.Contracts

# Add the source generator to Contracts and Domain projects
dotnet add PetStore.Api.Contracts package Atc.Rest.Api.SourceGenerator
dotnet add PetStore.Api.Domain package Atc.Rest.Api.SourceGenerator

⚙️ Step 3: Configure the Projects

📦 Configure Contracts Project

Edit PetStore.Api.Contracts.csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <!-- Make generated code visible for debugging -->
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Atc.Rest.Api.SourceGenerator" Version="*">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <!-- Tell the generator where to find the OpenAPI spec and config -->
  <ItemGroup>
    <AdditionalFiles Include="..\PetStore.yaml" />
    <AdditionalFiles Include=".atc-rest-api-server-contracts" />
  </ItemGroup>
</Project>

Create the marker file .atc-rest-api-server-contracts in the Contracts project:

{
  "generate": true,
  "validateSpecificationStrategy": "Standard",
  "includeDeprecated": false
}

🏢 Configure Domain Project

Edit PetStore.Api.Domain.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\PetStore.Api.Contracts\PetStore.Api.Contracts.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Atc.Rest.Api.SourceGenerator" Version="*">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <AdditionalFiles Include="..\PetStore.yaml" />
    <AdditionalFiles Include=".atc-rest-api-server-handlers" />
  </ItemGroup>
</Project>

Create the marker file .atc-rest-api-server-handlers in the Domain project:

{
  "generate": true,
  "generateHandlersOutput": "Handlers",
  "subFolderStrategy": "FirstPathSegment",
  "stubImplementation": "throw-not-implemented"
}

🔨 Step 4: Build and See Generated Code

dotnet build

After building, you'll see generated files in:

  • obj/Generated/Atc.Rest.Api.SourceGenerator/ (when EmitCompilerGeneratedFiles is true)

📄 What Gets Generated

The source generator creates these files for you:

File Description
PetStore.Pets.Models.g.cs 📦 C# record types (Pet)
PetStore.Pets.Parameters.g.cs 📥 Parameter DTOs with [FromQuery], [FromRoute]
PetStore.Pets.Results.g.cs 📤 Result classes with factory methods
PetStore.Pets.Handlers.g.cs 🎯 Handler interfaces (IListPetsHandler, etc.)
PetStore.Pets.Endpoints.g.cs 🛣️ Minimal API endpoint registrations
PetStore.DependencyInjection.g.cs 💉 DI extension methods

🎯 Step 5: Implement Your Handlers

Create Handlers/Pets/ListPetsHandler.cs in the Domain project:

using PetStore.Generated.Pets.Handlers;
using PetStore.Generated.Pets.Parameters;
using PetStore.Generated.Pets.Results;
using PetStore.Generated.Pets.Models;

namespace PetStore.Api.Domain.Handlers.Pets;

public sealed class ListPetsHandler : IListPetsHandler
{
    // Inject your dependencies
    private readonly IPetRepository repository;

    public ListPetsHandler(IPetRepository repository)
    {
        this.repository = repository;
    }

    public async Task<ListPetsResult> ExecuteAsync(
        ListPetsParameters parameters,
        CancellationToken cancellationToken = default)
    {
        // Your business logic here
        var pets = await repository.GetAllAsync(parameters.Limit, cancellationToken);

        // Return using the generated result factory
        return ListPetsResult.Ok(pets);
    }
}

Create Handlers/Pets/GetPetByIdHandler.cs:

public sealed class GetPetByIdHandler : IGetPetByIdHandler
{
    private readonly IPetRepository repository;

    public GetPetByIdHandler(IPetRepository repository)
    {
        this.repository = repository;
    }

    public async Task<GetPetByIdResult> ExecuteAsync(
        GetPetByIdParameters parameters,
        CancellationToken cancellationToken = default)
    {
        var pet = await repository.GetByIdAsync(parameters.PetId, cancellationToken);

        if (pet is null)
        {
            return GetPetByIdResult.NotFound();  // ❌ Returns 404
        }

        return GetPetByIdResult.Ok(pet);         // ✅ Returns 200 with pet
    }
}

🔌 Step 6: Wire Up Program.cs

Edit PetStore.Api/Program.cs:

using PetStore.Api.Domain;
using PetStore.Generated;

var builder = WebApplication.CreateBuilder(args);

// Add OpenAPI/Swagger support
builder.Services.AddOpenApi();

// Register all handler implementations from Domain project
builder.Services.AddApiHandlersFromDomain();

// Register your own services
builder.Services.AddSingleton<IPetRepository, InMemoryPetRepository>();

var app = builder.Build();

// Enable Swagger UI for development
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.UseSwaggerUI(options => options.SwaggerEndpoint("/openapi/v1.json", "PetStore API"));
}

// Map all generated endpoints with single call
app.MapApiEndpoints();

app.Run();

▶️ Step 7: Run Your API

cd PetStore.Api
dotnet run

Visit:

  • 📖 https://localhost:5001/swagger - Swagger UI
  • 🐾 https://localhost:5001/pets - List pets endpoint

🔧 Troubleshooting

❓ Generated files not appearing?

  1. Ensure marker files are in AdditionalFiles:
   <AdditionalFiles Include=".atc-rest-api-server-contracts" />
  1. Check the marker file has valid JSON

  2. Enable EmitCompilerGeneratedFiles to see generated files:

   <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  1. Look in obj/Generated/ folder after building

❓ Build errors about missing types?

  1. Ensure project references are correct:

    • Api -> Contracts
    • Api -> Domain
    • Domain -> Contracts
  2. The Contracts project needs Microsoft.NET.Sdk.Web SDK for ASP.NET Core types

❓ Handler not found at runtime?

  1. Ensure AddApiHandlersFromDomain() is called in Program.cs
  2. Check your handler implements the interface correctly
  3. Verify handler class is public and not abstract

➡️ Next Steps


📋 Quick Reference

📁 Marker Files

Marker File Purpose Project
.atc-rest-api-server-contracts Server code (models, endpoints) Contracts
.atc-rest-api-server-handlers Handler scaffolds Domain
.atc-rest-api-client-contracts HTTP client Client apps

💡 Tip: You can customize project names and namespaces using options in marker-files.

💉 Generated Extensions

Extension Method Purpose
AddApiHandlersFromDomain() Register all handler implementations
MapApiEndpoints() Map all generated endpoints
AddPetStoreEndpoints() Register client endpoint DI (EndpointPerOperation mode)

Clone this wiki locally