Files
Htmx/docs/01-getting-started.md
T
2026-05-05 23:55:26 +05:00

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.csdevkit in the Extensions panel.

  • C# (OmniSharp / Roslyn) — included with C# Dev Kit but can also be installed standalone as ms-dotnettools.csharp.

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 reflectionType.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 a JsonSerializerContext subclass (see AppJsonSerializerContext.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

  1. Open http://localhost:5120 in your browser.
  2. If you are not signed in, the middleware redirects you to /login — this is expected.
  3. Go to /register and create an account.
  4. 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.css as a pre-build step.
  • .htmx files 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:DefaultConnection in appsettings.json points to mongodb://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 publish regularly during development so issues do not pile up.