ASP.NET microservice archetype generator for .NET 10. Point it at an OpenAPI spec and get a ready-to-run hexagonal architecture solution.
Given an OpenAPI document with extended annotations, ApiGen produces a fully structured .NET solution:
| Layer | Namespace | Contents |
|---|---|---|
| Api | {Project}.Api |
Controllers, Helpers, Middleware, Program.cs |
| Domain | {Project}.Domain |
Models (DTOs), Services, Utils |
| Infrastructure | {Project}.Infrastructure |
Entities, Repositories, DbContext |
| Tests | {Project}.Domain.Tests |
Controller tests, Service tests |
Inspired by apigen.springboot, adapted for the .NET ecosystem by CloudAPPi Services.
dotnet run --project ./src/Api/Api.csprojdocker build -t apigen .
docker run -d -p 8080:8080 --name apigen apigendocker-compose up --build -dOnce running, the Swagger UI is available at /swagger. You can also trigger generation directly via curl — see the example specs under src/Generator/Examples/.
curl -X 'POST' \
'http://localhost:8080/generator/file' \
-H 'accept: */*' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@<openapi-file>'Download a prebuilt binary (see Standalone builds) or compile the Command project, then run:
apigen <openapi-path> -o <output-folder>- Positional argument: the input OpenAPI (
.yml/.yaml). -o/--outpath: output folder (default./). The CLI writes<name>.zipinside it.
The output folder must exist — the CLI does not create it.
mkdir -p out
apigen in/api-hospital.yml -o out/ # -> out/api-hospital.zipmacOS, Linux, WSL:
curl -fsSL https://raw.githubusercontent.com/apiaddicts/apigen.net/main/install.sh | shWindows PowerShell:
irm https://raw.githubusercontent.com/apiaddicts/apigen.net/main/install.ps1 | iexThe scripts detect your OS/arch, download the matching self-contained binary from
the latest GitHub Release, install it into
~/.apigen/bin (Unix) / %LOCALAPPDATA%\apigen\bin (Windows) and add it to your PATH.
Optional overrides: APIGEN_VERSION (e.g. 1.0.2) and APIGEN_INSTALL_DIR.
The one-liners only download a binary — they require the release assets to already exist (see Publishing a release).
The Linux binary is linux-x64 (glibc) and is published with
InvariantGlobalization, so it needs no ICU — only glibc + ca-certificates
(for the HTTPS calls that fetch NuGet versions). It runs on glibc images
(debian, ubuntu) but not on alpine (musl) or scratch (no libc). For
containers, prefer copying the binary directly:
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates && rm -rf /var/lib/apt/lists/*
COPY dist/apigen-dotnet-cli-linux-x64 /usr/local/bin/apigen
RUN chmod +x /usr/local/bin/apigen
ENTRYPOINT ["apigen"]To use the install.sh one-liner inside a Dockerfile instead, the base image also
needs curl/wget:
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://raw.githubusercontent.com/apiaddicts/apigen.net/main/install.sh | sh
ENV PATH="/root/.apigen/bin:${PATH}"
ENTRYPOINT ["apigen"]Alpine images need the musl binary (LinuxMusl profile → linux-musl-x64)
plus libstdc++ (the .NET runtime links against it and Alpine doesn't ship it by
default) and ca-certificates:
FROM alpine:3
RUN apk add --no-cache libstdc++ ca-certificates
COPY dist/apigen-dotnet-cli-linux-musl-x64 /usr/local/bin/apigen
RUN chmod +x /usr/local/bin/apigen
ENTRYPOINT ["apigen"]Without
libstdc++the binary fails at startup withError relocating ...: _ZTVSt9exception: symbol not found.
The CLI can be published as a self-contained, single-file, trimmed binary that embeds the .NET runtime — the resulting executable runs on a machine without .NET installed.
From the repo root:
dotnet publish src/Command/Command.csproj -p:PublishProfile=<Profile>Profiles live in src/Command/Properties/PublishProfiles/:
| Profile | RID | Executable |
|---|---|---|
FolderProfile |
win-x64 | apigen-dotnet-cli-win-x64.exe |
Linux |
linux-x64 | apigen-dotnet-cli-linux-x64 |
LinuxMusl |
linux-musl-x64 | apigen-dotnet-cli-linux-musl-x64 |
MacArm64 |
osx-arm64 | apigen-dotnet-cli-osx-arm64 |
MacIntel |
osx-x64 | apigen-dotnet-cli-osx-x64 (not published — see below) |
glibc vs musl:
Linux(linux-x64) targets glibc distros (Debian, Ubuntu, RHEL…).LinuxMusl(linux-musl-x64) targets musl distros — Alpine and Alpine-based container images. They are not interchangeable: a glibc binary won't run on Alpine and vice versa.Intel Macs (
osx-x64) are not part of the released binaries or the install script. TheMacIntelprofile still exists if you need to build one manually (-p:PublishProfile=MacIntel).
scripts/build-all.sh compiles win-x64, linux-x64 and osx-arm64 and
copies the binaries into a local dist/ folder:
./scripts/build-all.sh # all three -> dist/
./scripts/build-all.sh linux-x64 # a single one
# (win-x64 | linux-x64 | linux-musl-x64 | osx-arm64 | osx-x64)Result in dist/:
apigen-dotnet-cli-win-x64.exe (~29 MB)
apigen-dotnet-cli-linux-x64 (~18 MB)
apigen-dotnet-cli-osx-arm64 (~16 MB)
linux-musl-x64(Alpine) andosx-x64(Intel Mac) are not part ofall; build them on demand with-p:PublishProfile=LinuxMusl/MacIntel.
Cross-compiling downloads each RID's runtime pack on first use (needs internet).
Trimming shrinks the binary (~45 MB → ~27 MB on Windows), but several dependencies resolve types via reflection and would break if trimmed away. The profiles handle this with:
JsonSerializerIsReflectionEnabledByDefault=true— re-enables reflection-based JSON serialization (required byFileUtils.ReadOpenApi).TrimmerRootAssemblyentries preserving the reflection-heavy assemblies:Microsoft.OpenApi(.Readers),SharpYaml,CodegenCS.Core,CommandLine,Newtonsoft.JsonandNuGet.Protocol(+NuGet.Common,NuGet.Configuration,NuGet.Versioning,NuGet.Packaging,NuGet.Frameworks).
Without the NuGet.* roots the CLI fails when fetching the latest package versions
(NuGet.Protocol.Model.RegistrationIndex: Unable to find a constructor). The
IL2026 / IL2104 build warnings are static analysis over the preserved
assemblies; runtime behaviour is verified.
The install one-liners download binaries from GitHub Releases, so each version must be published there first. The two halves are independent:
- You (once per version): build the binaries and upload them as release assets.
- Everyone else (any time): run the install one-liner, which downloads them.
Steps (manual, via the GitHub web UI — no extra tooling required):
-
Build all platforms into
dist/:./scripts/build-all.sh # optionally also the Alpine/musl binary: dotnet publish src/Command/Command.csproj -p:PublishProfile=LinuxMusl cp src/Command/bin/Release/net10.0/publish/linux-musl-x64/apigen-dotnet-cli-linux-musl-x64 dist/ -
Go to
https://github.com/apiaddicts/apigen.net/releases→ Draft a new release. -
Create a tag matching the version, e.g.
1.0.2(should match<Version>inDirectory.Build.props). -
Drag the files from
dist/into the release assets area:apigen-dotnet-cli-win-x64.exeapigen-dotnet-cli-linux-x64apigen-dotnet-cli-osx-arm64apigen-dotnet-cli-linux-musl-x64(optional, Alpine)
-
Publish release.
Asset filenames must match exactly —
install.sh/install.ps1build the download URL from them (apigen-dotnet-cli-<rid>). The binaries are not committed to git (too large); they live only as release assets, which is whydist/is git-ignored.
Control the database provider via the data-driver field in the x-apigen-project OpenAPI extension:
| Value | NuGet package | DbContext registration |
|---|---|---|
| (not set) | Microsoft.EntityFrameworkCore.InMemory |
UseInMemoryDatabase(...) |
postgresql |
Npgsql.EntityFrameworkCore.PostgreSQL |
UseNpgsql(...) |
mysql |
Pomelo.EntityFrameworkCore.MySql |
UseMySql(..., ServerVersion.AutoDetect(...)) |
x-apigen-project:
name: My Project
description: ...
version: 1.0.0
data-driver: postgresql # postgresql | mysql | (omit for in-memory)The generated project includes only the required provider package. The connection string is read at runtime from the DATABASE_URL environment variable.
ApiGen relies on custom extensions to enrich the generated code:
| Extension | Scope | Purpose |
|---|---|---|
x-apigen-project |
Document | Project metadata and database driver |
x-apigen-models |
Components | Entity definitions with relational persistence mapping |
x-apigen-mapping |
Schema | DTO → Entity AutoMapper profile |
x-apigen-binding |
Path | Binds an endpoint group to a service |
If you prefer to scaffold entities from an existing database rather than defining them manually, use the EF Core CLI:
dotnet tool install --global dotnet-ef
dotnet ef dbcontext scaffold <connection-string> <driver> -o Infrastructure/EntitiesExample with PostgreSQL:
dotnet ef dbcontext scaffold \
"Host=<url>:<port>;Database=<db>;Username=<user>;Password=<pass>" \
Npgsql.EntityFrameworkCore.PostgreSQL \
-o Infrastructure/Entities