API Starter Guide

Create a production-ready REST API with JWT authentication, Scalar API documentation, health checks, structured logging, and comprehensive error handling using Noundry libraries.

Quick Start

# Clone the starter template
$ git clone https://github.com/noundry/NoundryApiStarter MyApi

# Navigate to the API project
$ cd MyApi/src/NoundryAPIStarter.Api

# Restore and run
$ dotnet restore
$ dotnet run

# API docs at https://localhost:5001/scalar/v1

What's Included

Authentication

  • JWT bearer authentication
  • API key support
  • Role-based authorization
  • Refresh token flow

Documentation

  • Scalar interactive API docs
  • OpenAPI 3.0 specification
  • Request/response examples
  • Auto-generated client SDKs

Observability

  • Structured logging (Serilog)
  • Health check endpoints
  • Request correlation IDs
  • Performance metrics

Production Ready

  • CORS configuration
  • Rate limiting
  • Response caching
  • Global error handling

Step-by-Step Guide

Step 1: Clone the Starter

Clone the Noundry API Starter from GitHub. All Noundry packages come pre-configured.

$ git clone https://github.com/noundry/NoundryApiStarter MyApi

Step 2: Restore Dependencies

Navigate to the API project and restore NuGet packages.

$ cd MyApi/src/NoundryAPIStarter.Api
$ dotnet restore

Step 3: Configure Database

Update appsettings.json with your connection string.

{
 "ConnectionStrings": {
 "DefaultConnection": "Host=localhost;Database=MyApiDb;Username=postgres;Password=secret"
 }
}

Step 4: Configure JWT Authentication

{
 "Jwt": {
 "Secret": "your-256-bit-secret-key-here",
 "Issuer": "MyApi",
 "Audience": "MyApiClients",
 "ExpirationMinutes": 60
 }
}

Step 5: Add Your First Endpoint

// Add a new endpoint group
var products = app.MapGroup("/api/products")
 .RequireAuthorization();

products.MapGet("/", async (
 [FromServices] IProductService service) =>
{
 var products = await service.GetAllAsync();
 return TypedResults.Ok(products);
});

products.MapPost("/", async (
 CreateProductDto dto,
 [FromServices] IProductService service) =>
{
 var product = await service.CreateAsync(dto);
 return TypedResults.Created(
 $"/api/products/{product.Id}", product);
});

Step 6: Run and Test

# Start the API
$ dotnet run

# Open Scalar API docs
# https://localhost:5001/scalar/v1

# Test with cURL
$ curl https://localhost:5001/api/products \
 -H "Authorization: Bearer YOUR_JWT_TOKEN"

Project Structure

MyApi/
 Endpoints/ # API endpoint groups
 Models/ # Entity models & DTOs
 Data/ # Tuxedo repositories, queries
 Services/ # Business logic
 Middleware/ # Error handling, logging
 Auth/ # JWT token service
 Program.cs # Minimal API entry point
 appsettings.json # Configuration
 Dockerfile # Container support

Program.cs Setup

The template uses .NET Minimal APIs with Noundry libraries pre-configured.

var builder = WebApplication.CreateBuilder(args);

// Add Noundry services
builder.Services.AddAuthnz(builder.Configuration);
builder.Services.AddTuxedo(builder.Configuration
 .GetConnectionString("DefaultConnection"));

// Add Scalar API documentation
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();

// Add health checks
builder.Services.AddHealthChecks();

var app = builder.Build();

// Middleware pipeline
app.MapOpenApi();
app.MapScalarApiReference();
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/health");

app.Run();

Middleware Configuration

Global Error Handling

Catches unhandled exceptions and returns consistent problem details (RFC 7807) JSON responses with correlation IDs for debugging.

Request Logging

Structured logging via Serilog with request correlation IDs, timing, and context enrichment for every HTTP request.

CORS Policy

Pre-configured CORS with environment-based allowed origins. Development allows localhost, production restricts to configured domains.

Rate Limiting

Built-in sliding window rate limiter with configurable limits per client IP and authenticated user to prevent abuse.

Testing Your API

# Authenticate and get a JWT token
$ curl -X POST https://localhost:5001/api/auth/login \
 -H "Content-Type: application/json" \
 -d '{"email": "admin@example.com", "password": "Password123!"}'

# Use the token for authenticated requests
$ curl https://localhost:5001/api/products \
 -H "Authorization: Bearer eyJhbGciOi..."

# Create a new resource
$ curl -X POST https://localhost:5001/api/products \
 -H "Authorization: Bearer eyJhbGciOi..." \
 -H "Content-Type: application/json" \
 -d '{"name": "Widget", "price": 9.99}'

# Check health
$ curl https://localhost:5001/health

Important: .NET 10 Minimal API Note

When mixing [FromForm] / IFormFile with service parameters in .NET 10 minimal APIs, you must annotate services with [FromServices]. Otherwise .NET infers them as Body parameters and crashes on startup with: "An action cannot use both form and JSON body parameters".

Perfect For

  • Microservices architecture
  • Mobile app backends
  • Third-party integrations
  • Public or partner APIs
  • Single-page application backends

Noundry Packages Included

<!-- Key packages in the API template -->
<PackageReference Include="Noundry.Authnz" />
<PackageReference Include="Noundry.Tuxedo" />
<PackageReference Include="Noundry.DotEnvX" />
<PackageReference Include="Noundry.Guardian" />
<PackageReference Include="Noundry.Sod" />

Noundry.Authnz

JWT authentication, API keys, and role-based authorization.

Noundry.Tuxedo

Lightweight ORM with Dapper-compatible API for database access.

Noundry.DotEnvX

Environment variable management with encryption for secrets.

Noundry.Guardian

Request pipeline, exception handling, and observability.

Noundry.Sod

Input validation with fluent API and automatic error responses.

Deployment

Use Noundry Spinup to provision a security-hardened VM on any cloud, then deploy your API with Docker.

1. Provision a VM with Spinup

# Install Spinup
$ dotnet tool install -g Noundry.Spinup

# Set your cloud credentials (example: DigitalOcean)
$ export DIGITALOCEAN_TOKEN=your-api-token

# Provision a VM (AWS, Azure, GCP, or DigitalOcean)
$ spinup provision --cloud digitalocean --region nyc3 --resource vm --name myapi -y

# Or use AWS
$ export AWS_ACCESS_KEY_ID=your-key
$ export AWS_SECRET_ACCESS_KEY=your-secret
$ spinup provision --cloud aws --region us-east-1 --resource vm --name myapi -y

Spinup automatically hardens the VM with UFW firewall, fail2ban, SSH key-only auth, and your IP whitelisted.

2. Build and Deploy

# Build for production
$ dotnet publish -c Release -o ./publish

# Or use Docker
$ docker build -t myapi .

3. SSH and Run

# SSH into your VM (key auto-generated by Spinup)
$ ssh -i ~/.spinup/workspaces/myapi/myapi_ssh_key.pem ubuntu@<public-ip>

# Deploy with Docker on the VM
$ docker run -d -p 80:80 --env-file .env myapi

Production Checklist

  • Set a strong JWT secret (at least 256-bit)
  • Configure CORS allowed origins for your domain
  • Enable HTTPS with a valid certificate
  • Set environment-specific connection strings
  • Configure rate limiting thresholds
  • Enable structured logging to a centralized service

Related Resources