Skip to content

Getting Started with Basic

davidkallesen edited this page Feb 4, 2026 · 5 revisions

πŸš€ 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, file uploads dotnet add package Atc.Rest.Client
<!-- Add to .csproj if using EndpointPerOperation mode or file uploads -->
<ItemGroup>
  <PackageReference Include="Atc" Version="3.*" />
  <PackageReference Include="Atc.Rest.Client" Version="2.*" />
</ItemGroup>

πŸ’‘ Note: The TypedClient mode (default) does not require these packages unless you're using file uploads (IFileContent). Only add them if using EndpointPerOperation generation mode or working with file upload operations.


🎯 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
β”‚   └── 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
β”‚   └── (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

πŸ“ 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" />
  </ItemGroup>
</Project>

Create the marker file .atc-rest-api-server 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" />
  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

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

πŸ’‘ Tip: You can customize project names and namespaces using options in marker files. See Marker Files for complete configuration reference.

πŸ’‰ Generated Extensions

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

🏠 Home

πŸ’Ό Why This Tool?

πŸ“– Getting Started

βš™οΈ Features

🌐 Frontend

πŸ“‹ Reference


πŸ”— Resources

Clone this wiki locally