Co-authored-by: Copilot <copilot@github.com>
7.9 KiB
Getting Started
This guide walks you through everything you need to get the project running locally — from installing tools to understanding why certain architectural decisions were made.
What is in this solution
| Project | Purpose |
|---|---|
Htmx.ApiDemo |
ASP.NET Core web app using Minimal APIs and server-rendered HTMX templates |
Htmx.SourceGenerator |
Roslyn source generator that reads .htmx template files and generates endpoint mapping code at build time |
The solution file is Htmx.slnx at the repository root.
Prerequisites
Install the following before cloning the repo.
.NET SDK
The project targets net10.0. Download the .NET 10 SDK from dot.net.
Verify with:
dotnet --version
Node.js and npm
Tailwind CSS is compiled during the build using the Tailwind CLI via npx. Node.js must be installed.
Download from nodejs.org. Verify with:
node -v
npm -v
MongoDB
The app stores data in MongoDB. You need a local instance running on mongodb://localhost:27017.
Windows:
Download and install MongoDB Community Server. During installation, choose to run MongoDB as a Windows Service so it starts automatically.
Linux:
Follow the official guide for your distro at docs.mongodb.com/manual/administration/install-on-linux. For Ubuntu/Debian:
sudo systemctl start mongod
sudo systemctl enable mongod # start on boot
MongoDB Compass (optional but recommended):
Compass is a GUI for browsing and querying your MongoDB data. Download it from mongodb.com/products/compass. Connect it to mongodb://localhost:27017 to inspect collections while developing.
VS Code setup
Required extensions
-
C# Dev Kit — provides IntelliSense, debugging, and project support for .NET Search for
ms-dotnettools.csdevkitin the Extensions panel. -
C# (OmniSharp / Roslyn) — included with C# Dev Kit but can also be installed standalone as
ms-dotnettools.csharp.
Recommended settings
Add the following to your workspace or user settings.json. This teaches VS Code to treat .htmx files as HTML (for syntax highlighting and formatting) and nests generated companion files under their parent in the Explorer sidebar so the file tree stays clean.
{
"files.associations": {
"*.htmx": "html"
},
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"*.razor": "$(capture).razor.cs, $(capture).razor.css, $(capture).razor.js",
"*.htmx": "${capture}.htmx.cs, ${capture}.htmx.routing.cs, ${capture}.g.cs, ${capture}.css"
}
}
Without files.associations, .htmx files open as plain text with no highlighting. Without file nesting, every generated .htmx.cs and .htmx.routing.cs file appears as a separate top-level entry in the Explorer, making it hard to navigate.
Optional extension
- Tailwind CSS Fold — collapses long Tailwind class strings in the editor so markup is easier to read. Search for
stivo.tailwind-fold. This is purely a cosmetic convenience and has no effect on the build.
Understanding AOT
AOT (Ahead-of-Time compilation) means the app is compiled to native machine code before it runs, rather than relying on the .NET JIT at runtime. This project has AOT enabled (<PublishAot>true</PublishAot> in the .csproj).
Why AOT matters here
AOT produces smaller, faster deployments with no JIT warmup time. For a web app handling many requests, startup time and binary size are real concerns — especially in containerized or serverless environments.
What AOT prevents you from doing
AOT is a significant constraint. It eliminates entire categories of patterns that are common in standard .NET development:
-
No Entity Framework Core — EF Core relies heavily on runtime reflection and expression compilation. It is not AOT-compatible. This project uses the MongoDB driver directly instead.
-
No runtime reflection —
Type.GetProperties(),Activator.CreateInstance(), dynamic proxies, and similar patterns do not work (or produce warnings/errors) under AOT. If a pattern depends on inspecting types at runtime, it will not survive. -
Many NuGet packages are incompatible — Any package that uses reflection internally (serializers, mappers, validators, ORMs, DI containers with convention scanning, etc.) may break. Check a package's AOT compatibility before adding it.
-
Source generator-based serialization required — Rather than
JsonSerializer.Serialize(myObject)discovering properties at runtime, you must register types with aJsonSerializerContextsubclass (seeAppJsonSerializerContext.cs). The serializer then uses generated code instead of reflection. -
Route handler code generation — ASP.NET Core's Minimal API generator produces code for request binding and response writing. Some third-party packages produce code that conflicts with this. If adding a package causes build errors in generated files, AOT incompatibility is the likely cause.
The practical rule: before reaching for a package or pattern you know from standard ASP.NET Core, check whether it is AOT-compatible. The project will compile normally in Debug mode even with AOT-incompatible code — AOT issues typically only surface during dotnet publish.
First-time setup
Clone the repo, then install the npm dependencies that the build needs:
cd Htmx.ApiDemo
npm install
This installs the Tailwind CSS CLI package. The build runs npx @tailwindcss/cli as an MSBuild step, so if this is skipped the build will fail with a missing command error.
Run the app
From the repository root:
dotnet run --project Htmx.ApiDemo/Htmx.ApiDemo.csproj
The app listens on http://localhost:5120 by default (configured in Htmx.ApiDemo/Properties/launchSettings.json).
Verify it works
- Open
http://localhost:5120in your browser. - If you are not signed in, the middleware redirects you to
/login— this is expected. - Go to
/registerand create an account. - Sign in and explore the app.
What happens at startup
Program.cs wires up the following in order:
- MongoDB service registration and index initialization (
EnsureIndexesAsync) - Cookie-based authentication and authorization
- Antiforgery middleware
- AOT-compatible JSON serialization via
AppJsonSerializerContext - All generated HTMX endpoints via
app.MapHtmxApiDemoEndpoints()
Build details
- Tailwind is compiled into
wwwroot/css/output.cssas a pre-build step. .htmxfiles are passed to the source generator as<AdditionalFiles>. The generator reads them and produces the abstract base classes and routing code.- AOT is active on publish. Run a publish build early and often to catch incompatibilities before they accumulate.
Publish (AOT)
dotnet publish Htmx.ApiDemo/Htmx.ApiDemo.csproj -c Release
Troubleshooting
Build fails — Tailwind command not found
Run npm install inside the Htmx.ApiDemo directory. Confirm node and npm are on your PATH.
MongoDB connection errors at startup
- Confirm the MongoDB service is running (
mongod). - Check that
ConnectionStrings:DefaultConnectioninappsettings.jsonpoints tomongodb://localhost:27017.
App always redirects to /login
This is intentional. Unauthenticated requests are redirected by the auth middleware. Register at /register first.
AOT warnings or errors on publish
- Look at the warning message — it usually names the type or method causing the issue.
- Remove or replace the offending package or pattern with an AOT-compatible alternative.
- Run
dotnet publishregularly during development so issues do not pile up.