Im Inneren von DenoX: Dateirouting, MVC-Slices und ein AGENTS.md-Vertrag in Deno
Wie das DenoX-Framework Hono, dateibasiertes Routing, Feature-Slices, globales Sicherheits-Middleware sowie einen auf Spezifikationen basierenden AGENTS.md-Arbeitsablauf für KI-Code-Agenten kombiniert.
Das Verbinden eines Servers ist selten der wertvolle Teil eines Backend-Projekts – vielmehr zählt das Bereitstellen neuer Funktionen. Rails, Laravel und Next.js gewannen Entwickler, indem sie die notwendigen Entscheidungen für sie trafen, und Deno mit seinen standardmäßig sicheren Berechtigungen, nativen TypeScript-Fähigkeiten sowie integrierten Werkzeugen ist ein idealer Kandidat für eine ähnliche Herangehensweise. DenoX ist ein Open-Source-Full-Stack-Framework für Deno, das auf Hono basiert und genau diese vorgefertigte Schicht darstellen soll. Seine Antworten auf wiederkehrende strukturelle Fragen sowie die Art und Weise, wie er sie für KI-Code-Agenten festhält, sind Muster, die in jedem TypeScript-Backend wiederverwendet werden können.
Die Lücke, die eine vorgefertigte Schicht füllt
Deno 2 brachte Kompatibilität zu npm, das JSR-Registry, eine ausgereifte Standardbibliothek sowie ein einziges Binärprogramm, das Linting, Formatierung, Tests, Kompilieren und Bündeln ermöglicht (siehe wie Deno 2.x die Node-Kompatibilitäts- und Tooling-Probleme gelöst hat für Hintergrundinformationen). Was ein reiner Laufzeitumgebung nicht bieten kann, ist Einigkeit zu den Fragen, über die jedes Team streitet: Wo die Geschäftslogik gehören soll, wie Fehler gemeldet werden, wer die Konfiguration überprüft und wo die Rate-Limiting-Regeln anwendbar sind.
DenoX beantwortet diese Fragen mit einem einzigen Prinzip: Konventionen statt Konfiguration, überprüft durch Tools. Dokumentierte Konventionen ändern sich; Konventionen, die in CI überprüft werden, bleiben konstant.
Dateibasiertes Routing mit einer generierten, gespeicherten Tabelle
Die Routen stammen aus dem Dateisystem. Das Hinzufügen einer Datei im pages-Verzeichnis ergibt eine URL, wobei die in Klammern gesetzten Abschnitte zu Parametern werden:
src/frontend/pages/
├── index.ts → /
├── about/main.ts → /about
├── users/main.ts → /users
└── posts/[id].ts → /posts/:id
Die Entdeckung der Routen erfolgt nicht zur Laufzeit. Das Ausführen von deno task routes durchläuft den Routenbaum und erzeugt eine statische, deterministische Routentabelle. Zwei Aspekte sorgen für Robustheit:
- Statische Routen werden immer vor dynamischen registriert, sodass
/users/newniemals von/users/:idüberlagert werden kann. Bei Routen-Engines wie Hono, die nach dem Prinzip des ersten Treffers arbeiten, bestimmt die Registrierungsreihenfolge das Verhalten – ihre automatische Erstellung beseitigt eine klassische Quelle für subtile Fehler. - Die erzeugte Datei wird im Repository gespeichert, und der CI-Test fehlschlägt, wenn sie veraltet ist.
Seiten sind einfache Funktionen
Eine Seite ist ein gewöhnliches TypeScript-Modul. Es importiert den Context-Typ von Hono sowie einen Hilfsfunktionen zum Entschlüsseln von HTML:
import type { Context } from "hono";
import { escapeHtml } from "@/shared/html.ts";
Daraufhin exportiert es ein config-Objekt, das ein Layout sowie eine Standardfunktion auswählt, die einen HTML-String zurückgibt:
export const config = { layout: "default" } as const;export default function homePage(c: Context): string {
const name = escapeHtml(c.req.query("name") ?? "world");
return `<h1>Hello, ${name}!</h1>`;
}
Der bemerkenswerte Aspekt ist escapeHtml. Der Abfrageparameter name wird vom Benutzer gesteuert, und seine direkte Einbettung in die Markup-Struktur würde ein klassisches reflected XSS-Sicherheitsloch darstellen. In DenoX ist das Entschlüsseln unzuverlässiger Eingaben keine Empfehlung, sondern eine Vorschrift, die im ingenieurtechnischen Vertrag des Projekts festgelegt ist. Da die Seiten Rohstrings ohne automatisches Entschlüsseln durch einen Template-Engine zurückgeben, muss jede Einbettung externer Daten über diesen Hilfsfunktion erfolgen.
Funktionsbereiche mit fester Struktur
Auf der API-Seite ist jede Funktion ein eigenständiger Funktionsbereich mit derselben Menge an Dateien, wobei jede Datei eine einzige Aufgabe hat:
src/api/users/
├── user.model.ts entities only
├── user.dto.ts unknown → typed DTO (boundary validation)
├── user.repository.ts interface + default implementation
├── user.service.ts business rules only — no HTTP, no HTML
├── user.controller.ts HTTP adapter only
└── user.routes.ts composition root (constructor injection)
Das DTO-Modul wandelt Eingaben vom Typ unknown an der Grenze in typisierte Objekte um, sodass keine weiteren Komponenten im Stack mit rohen Anfragekörpern arbeiten müssen. Services enthalten ausschließlich Geschäftsregeln und kennen weder HTTP noch HTML. Controller sind schlanke HTTP-Adapter. Die Routes-Datei ist der Zusammenstellungspunkt, an dem Abhängigkeiten über Konstruktorinjektion verknüpft werden.
Services sind von Repository-Schnittstellen und nicht von konkreten Klassen abhängig. Der Ersatz des In-Memory-Speichers durch Postgres oder Deno KV bedeutet daher, dass pro Funktionalität nur eine Datei geändert werden muss.
Fehler als typisierte Ausnahmen
Geschäftsregeln signalisieren Fehler durch das Werfen typisierter Ausnahmen. Die untenstehende Service-Methode weigert sich, einen zweiten Benutzer mit einer bereits vorhandenen E-Mail-Adresse zu erstellen:
async create(dto: CreateUserDto): Promise<User> {
const existing = await this.repository.findByEmail(dto.email);
if (existing !== null) {
throw new ConflictException(`Email "${dto.email}" is already registered`);
}
return await this.repository.create(dto);
}
Dienst wählt weder einen Statuscode aus noch formatiert er eine Antwort. Ein zentraler Fehlerhandler ordnet jeden Ausnahmetyp einer einheitlichen JSON-Struktur zu und sorgt dafür, dass Stacktraces niemals die Clients erreichen. Beachten Sie, dass das Zurücksenden der E-Mail eine Auflistung von Konten ermöglicht, was an öffentlichen Endpunkten vermieden werden sollte.
Sicherheit einmal implementiert, überall angewendet
Kreuzfunktionale Schutzmaßnahmen befinden sich im globalen Middleware-Modul und nicht in jeder Funktion: eine Content Security Policy, verschärfte Antwortheader, CORS-Regeln, CSRF-Prüfungen basierend auf der Herkunft, Bandbreitenbeschränkungen nach Klienten-IP, Obergrenzen für die Größe des Anhangs, Zeitlimits sowie Maskierung interner Fehler. Funktionen nutzen diese Schutzmaßnahmen, anstatt sie erneut zu implementieren.
Auch die Konfiguration wird auf dieselbe Weise behandelt. Jede Umgebungsvariable wird analysiert, überprüft und beim Start des Prozesses festgelegt; die Anwendung weigert sich, zu starten, wenn etwas fehlt oder fehlerhaft ist. In der Produktion wird CORS_ORIGIN=* vollständig abgelehnt. Schnelles Scheitern beim Start ist besser, als einen halb konfigurierten Dienst in der Produktion zu debuggen.
Drei Testebenen hinter einem Befehl
Die Testumgebung geht über eine einfache Platzhalterprüfung hinaus:
- Unit-Tests überprüfen die reine Logik mithilfe von Mocks zur Aufzeichnung von Aufrufen und benötigen keinerlei Deno-Rechte.
- Integrationstests testen die vollständig vernetzte Anwendung über
app.request(), wobei Statuscodes, Antwortstrukturen und sogar Sicherheitsheader überprüft werden – ohne dass ein Socket geöffnet wird.
Deno.serve auf einem temporären Port und rufen diesen mit echten fetch-Anfragen auf, wobei eine Anfrage absichtlich den Rate-Limiter auslöst, um zu überprüfen, ob 429 zurückgegeben wird.Das umfassende Qualitätskontrollsystem, das Formatierung, Linting, die Überprüfung der veralteten Route-Tabelle, strenge Typüberprüfungen sowie alle Testschichten umfasst, wird mit deno task ci ausgeführt, und der GitHub Actions-Pipeline führt genau diese Abfolge aus.
Ein einziger Deploy-Befehl, keine Verwaltung von Zugangsdaten
Das Repository enthält Manifeste für Fly.io, Railway, Render, Docker sowie eine gesicherte systemd-Einheit für einen VPS, zusätzlich zur erstklassigen Unterstützung von Deno Deploy. Eine einzige Aufgabe listet die Ziele auf, führt einen Probelauf durch oder vollzieht den Deployment:
deno task deploy # list targets
deno task deploy fly # dry run: steps + env reminders
deno task deploy fly --run # execute (auth delegated to the platform CLI)
Das Deployment-Tool behandelt Absichtlich niemals Anmeldeinformationen. Es überprüft die Voraussetzungen, zeigt den Plan zusammen mit Erinnerungen an erforderliche Umgebungsvariablen und überlässt die Authentifizierung der offiziellen CLI jeder Plattform. Geheime Informationen bleiben völlig außerhalb des Frameworks.
AGENTS.md als durchsetzbarer Ingenieurvertrag
Der auffälligste Aspekt von DenoX ist eine AGENTS.md-Datei im Wurzelverzeichnis des Repositoriums, die als verbindlicher Vertrag sowohl für menschliche Mitwirkende als auch für KI-Programmieragenten dient. Sie legt den Technologiestack fest, definiert den kanonischen Verzeichnisbaum und listet die gemeinsam genutzten Grundkomponenten auf, die niemals neu entwickelt werden dürfen: der Logger, die Ausnahmehierarchie, das Antwortformat sowie das Konfigurationsmodul.
Zusätzlich wird darin ein auf Spezifikationen basierender Entwicklungsprozess festgelegt:
- Eine Spezifikation wie
specs/feature.mdwird mitstatus: drafterstellt. - Ein Mensch prüft sie und ändert den Status auf
status: approved. - Nur nach der Freigabe geht die Arbeit weiter mit Architektur, Planung, Implementierung, Tests und Dokumentation.
Den Agenten wird ausdrücklich mitgeteilt, dass sie aufhören sollen, sobald die Spezifikation geschrieben wurde, und auf eine Freigabe durch einen Menschen warten müssen; dadurch kann ein Agent seinen eigenen Plan nicht freigeben und anschließend die Hälfte des Codebases umschreiben. Ein vollständiger Referenzzyklus für die Benutzermanagement zeigt den Agenten dieses Muster, und CI durchsetzt die Konventionen mechanisch – indem der Build fehlschlägt, sobald eine generierte Datei von Hand geändert wurde.
Da die Agenten immer mehr Code schreiben, sind Konventionen nur dann von Bedeutung, wenn sie automatisch überprüft werden können; das Versionieren des Kontrakts neben dem Code in Kombination mit CI verwandelt Leitlinien in Schutzmechanismen. Für eine ähnliche Sichtweise zu Anweisungsdateien für Assistenten siehe Vercels AGENTS.md-Fähigkeit für React-Best Practices.
Lokale Ausführung
Klonen Sie das Repository, erstellen Sie eine Umgebungsdatei anhand des Beispiels und starten Sie den Entwicklungsserver:
git clone https://github.com/olavomello/denox.git
cd denox
cp .env.example .env
deno task dev
Öffnen Sie anschließend http://localhost:8000, rufen Sie /api/users auf, senden Sie absichtlich ungültige Daten und überprüfen Sie, ob die Fehlermeldung sauber bleibt und keine Stack-Traces enthält. Es steht auch eine Live-Version zur Verfügung. Das Projekt ist unter der MIT-Lizenz veröffentlicht; zu seinem geplanten Entwicklungsplan gehören Adapter für Deno KV und Postgres, automatische Registrierung von Layouts, eine eigene CLI, ein Authentifizierungsmodul sowie die Erstellung von OpenAPI-Dokumentationen – alles soll nach demselben prozessorientierten Vorgehen erstellt werden. Überprüfen Sie vor der Nutzung den aktuellen Zustand des Repositoriums.
Haupterkenntnisse
- Erstellen Sie Routentabellen zur Build-Zeit, registrieren Sie statische Routen vor dynamischen, speichern Sie die Ergebnisse ab und lassen Sie CI veraltete Dateien ablehnen.
- Geben Sie jeder Funktion eine feste Struktur: Grenz-DTOs, auf Schnittstellen basierende Repositorien, services ohne HTTP-Aufrufe sowie schlanke Controller.
AGENTS.md, das eine menschliche Freigabe der Spezifikationen erfordert, und setzen Sie dessen Regeln in CI durch, damit sie sowohl Agenten als auch Menschen binden.