Skip to content

toraaoo/platform-api

Repository files navigation

Platform.Api

.NET 9 ASP.NET Core PostgreSQL EF Core CI

Starter ASP.NET Core Web API built on .NET 9, Entity Framework Core, and PostgreSQL.

The repository is organized as a small layered backend template with:

  • API, application, domain, and infrastructure project boundaries
  • consistent JSON response envelopes for success, validation, pagination, and errors
  • PostgreSQL + EF Core wiring with migrations
  • health checks and centralized exception handling
  • integration and domain tests

Solution layout

src/
├── Platform.Api             # HTTP entrypoint, controllers, contracts, exception handling
├── Platform.Application     # Application abstractions and service registration
├── Platform.Domain          # Domain entities and rules
└── Platform.Infrastructure  # EF Core, DbContext, migrations, database options

tests/
├── Platform.Api.Tests
└── Platform.Application.Tests

Current API surface

The project currently exposes these endpoints:

  • GET /
  • GET /health
  • GET /api/system/info
  • POST /api/system/echo
  • GET /api/system/paged-sample?page=1&pageSize=2
  • GET /api/system/throw/{kind}

{kind} supports:

  • validation
  • notfound
  • conflict
  • forbidden
  • any other value returns a simulated 500

OpenAPI is enabled in development. The raw OpenAPI document is exposed at /openapi/v1.json, and interactive Scalar docs are available at /docs.

Domain sample

The sample domain currently includes a WorkItem entity with:

  • trimmed title and description handling
  • audit timestamps from AuditableEntity
  • completion state tracking
  • EF Core mapping to the work_items table

Prerequisites

  • .NET SDK 9.0.306 or compatible 9.0.x
  • PostgreSQL 16
  • Docker (optional, for running PostgreSQL in a container)

The SDK version pinned in global.json is 9.0.306.

Getting started

Quick setup (recommended)

Run the automated setup script to configure everything:

# First time: restore dotnet tools
dotnet tool restore

# Run setup script
dotnet script scripts/setup.csx

The script will:

  • Auto-detect existing .env file and use its values, or prompt you if missing
  • Check prerequisites (.NET SDK, Docker)
  • Generate secure JWT secret key if needed (auto-updates .env)
  • Configure .NET User Secrets with your values
  • Create .env file for Docker Compose if it doesn't exist
  • Start PostgreSQL via Docker Compose (if Docker available)
  • Apply database migrations

After setup completes, start the API:

dotnet run --project src/Platform.Api/Platform.Api.csproj

Manual setup

If you prefer to configure manually:

1. Start PostgreSQL

Using Docker:

docker compose up postgres -d

Or use your local PostgreSQL installation.

2. Configure secrets

cd src/Platform.Api

# Set database connection string
dotnet user-secrets set "ConnectionStrings:DefaultConnection" \
  "Host=localhost;Port=5432;Database=platform_api;Username=postgres;Password=YOUR_PASSWORD"

# Set JWT configuration (secret key must be at least 32 characters)
dotnet user-secrets set "Jwt:SecretKey" "your-secret-key-minimum-32-characters"
dotnet user-secrets set "Jwt:Issuer" "Platform.Api"
dotnet user-secrets set "Jwt:Audience" "Platform.Api"
dotnet user-secrets set "Jwt:AccessTokenExpirationMinutes" "60"
dotnet user-secrets set "Jwt:RefreshTokenExpirationDays" "7"

3. Apply migrations

./scripts/migrate.sh apply

Or using the Makefile:

make migrate apply

4. Run the API

dotnet run --project src/Platform.Api/Platform.Api.csproj

Docker Compose (full stack)

To run the complete stack with Docker:

# Copy and configure environment file
cp .env.example .env
# Edit .env and set your values

# Start all services
docker compose up

The API will be available at http://localhost:5000.

Configuration

The API uses standard ASP.NET Core configuration precedence with fail-fast validation:

  • Local development: Uses .NET User Secrets for sensitive values (connection strings, JWT secrets)
  • Docker: Uses .env file for Docker Compose variable interpolation
  • Base configuration: appsettings.json and appsettings.Development.json contain only non-sensitive defaults

The app validates required configuration at startup and fails with clear error messages if values are missing.

To view configured user secrets:

dotnet user-secrets list --project src/Platform.Api

Local development URLs:

  • http://127.0.0.1:5000
  • https://127.0.0.1:5001

API documentation (development only):

  • http://127.0.0.1:5000/docs
  • https://127.0.0.1:5001/docs
  • Raw OpenAPI JSON: /openapi/v1.json

Development commands

Build

dotnet restore Platform.Api.sln
dotnet build Platform.Api.sln -m:1 -nr:false

Or using make:

make restore
make build

Database migrations

Create a new migration:

./scripts/migrate.sh create YourMigrationName
# Or: make migrate NAME=YourMigrationName

Apply migrations:

./scripts/migrate.sh apply
# Or: make migrate apply

Response contract

The API uses a shared envelope with these top-level fields:

  • success
  • message
  • data
  • pageMeta
  • errors
  • traceId

Top-level nullable fields are omitted from the serialized JSON when their value is null. In practice:

  • success responses usually include data
  • paginated responses include both data and pageMeta
  • validation responses include errors
  • unhandled server errors include traceId

Example success response:

{
  "success": true,
  "message": "System information retrieved successfully.",
  "data": {
    "service": "Platform.Api",
    "status": "ready",
    "utc": "2026-03-31T12:00:00+00:00"
  }
}

Example validation response:

{
  "success": false,
  "message": "Validation failed.",
  "errors": [
    {
      "field": "Message",
      "messages": [
        "The Message field is required."
      ]
    }
  ]
}

Example paginated response:

{
  "success": true,
  "message": "Paged sample retrieved successfully.",
  "data": [
    {
      "id": 4,
      "name": "Item 4"
    }
  ],
  "pageMeta": {
    "page": 2,
    "pageSize": 3,
    "totalItems": 10,
    "totalPages": 4
  }
}

Example unhandled error response:

{
  "success": false,
  "message": "An unexpected error occurred.",
  "traceId": "0HNABCDE12345:00000002"
}

Exception mapping

Unhandled exceptions are translated centrally in src/Platform.Api/Program.cs:

  • ValidationException -> 400 Bad Request
  • KeyNotFoundException -> 404 Not Found
  • InvalidOperationException -> 409 Conflict
  • UnauthorizedAccessException -> 403 Forbidden
  • any other unhandled exception -> 500 Internal Server Error

404 responses from unknown routes are also wrapped in the same JSON error envelope.

Testing

Run all tests with dotnet:

dotnet test Platform.Api.sln -m:1 -nr:false

Or with make:

make test

Current tests cover:

  • root endpoint response shape
  • health endpoint response shape
  • wrapped success responses
  • paginated responses
  • validation failures
  • unknown routes
  • exception-to-status-code mapping
  • WorkItem domain behavior

Because the API always wires PostgreSQL and a database health check during startup, local test execution expects a valid configured connection string and a reachable PostgreSQL instance unless the test host setup is changed.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages