.NET 10 web app serving an Angular client. Backend uses Dapper + Npgsql against PostgreSQL with SQL migrations applied on startup via DbUp.
- .NET 10 (single project,
claude-starter.csproj) - PostgreSQL
- Dapper + Npgsql (repository pattern, no EF Core)
- DbUp for migrations (embedded
Migrations/Scripts/*.sql) - BCrypt.Net-Next for password hashing
- Cookie authentication (ASP.NET Core), Data Protection keys persisted in Postgres
- Angular 21 client in
ClientApp/, served as static files
Program.cs App wiring, auth, rate limiting, SPA fallback
Endpoints/ Minimal API endpoints — one endpoint per file
Auth/ /api/auth login, logout, register, me
Repositories/ Dapper repositories (no EF Core)
Services/ BCrypt password hashing, Postgres-backed Data Protection
Data/ Npgsql connection factory + Dapper config
Models/ Domain types
Migrations/
DbMigrator.cs DbUp runner (applied on startup)
Scripts/*.sql Embedded migration scripts, applied in name order
ClientApp/ Angular 21 client
All routes are rate-limited and live under /api/auth:
| Method | Route | Auth | Purpose |
|---|---|---|---|
| POST | /api/auth/register |
— | Create a user |
| POST | /api/auth/login |
— | Sign in, sets auth + XSRF-TOKEN cookies |
| POST | /api/auth/logout |
required | Sign out |
| GET | /api/auth/me |
required | Current user |
There is no seed user — register one to get started.
- .NET 10 SDK
- Docker (or a local Postgres instance)
- Node.js + npm (for the Angular client)
Connection string is read from ConnectionStrings:Postgres. Default in appsettings.json points at localhost:5432 as postgres / postgres. Override via env var:
ConnectionStrings__Postgres=Host=db;Port=5432;Database=claude_starter;Username=...;Password=...
Start Postgres via Docker Compose:
docker compose up -dTear down (keep data):
docker compose downTear down and wipe the volume:
docker compose down -vInstall Angular dependencies (first run):
cd ClientApp
npm install
cd ..Two terminals, both same-origin (Angular output served from wwwroot by .NET — no ng serve, no proxy):
# Terminal 1 — rebuild Angular on change into dist/
cd ClientApp
npm run watch
# Terminal 2 — run API (also serves the Angular bundle and SPA fallback)
dotnet runOn startup the app ensures the database exists, then DbUp applies any pending scripts from Migrations/Scripts/ and tracks them in the schemaversions table. Re-running is idempotent.
cd ClientApp
npm run build
cd ..
dotnet run# Backend (integration tests need Docker running for Testcontainers)
dotnet test
# Frontend
cd ClientApp
npm testCreate Migrations/Scripts/NNNN_description.sql (zero-padded sequence). The file is automatically included as an embedded resource. DbUp applies scripts in name order on next startup.
MIT — see LICENSE.
Click Use this template → Create a new repository. A Template bootstrap GitHub Action runs on the new repo, derives the project name from the repository name (coerced to kebab-case), renames everything, and commits the result as chore: bootstrap project from template. Watch it under the Actions tab.
The workflow leaves itself in place (a GitHub token can't delete workflow files), but a sentinel check makes it a no-op on every later push. Delete .github/workflows/template-bootstrap.yml by hand once you're set up if you'd like it gone.
If your repository name can't be coerced to a valid kebab-case name (^[a-z][a-z0-9-]{1,49}$), the workflow fails with a clear error — rename the repo and re-run it from the Actions tab.
Clone the repo, then rename the project to your own name. The rename scripts replace every claude-starter / claude_starter placeholder in source + filenames, strip this section, delete .git/, clean bin/ and obj/, and self-delete.
Linux / macOS:
./rename-project.sh my-new-appWindows (PowerShell):
./rename-project.ps1 my-new-appName must be kebab-case, 2-50 chars, starting with a letter (^[a-z][a-z0-9-]{1,49}$). The snake-case form (my_new_app) is derived automatically for namespaces and the Postgres database name.
Flags: --yes / -y skip the confirmation prompt; --force bypasses the safety guard that checks you are still in the template directory.
After it finishes:
git init && git add -A && git commit -m "Initial commit"
cd ClientApp && npm install
dotnet restore my-new-app.sln