EmDash integriert sich tief in Astro, um ein vollständiges CMS-Erlebnis zu bieten. Diese Seite erklärt die wichtigsten Architekturentscheidungen und wie die Teile zusammenpassen.
Überblick auf hoher Ebene
┌──────────────────────────────────────────────────────────────────┐
│ Ihre Astro-Site │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ EmDash Integration │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ Content │ │ Admin │ │ Plugins │ │ │
│ │ │ APIs │ │ Panel │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └───────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Datenschicht │ │ │
│ │ │ Datenbank (D1/SQLite) + Speicher (R2/S3) │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Astro Framework │ │
│ │ Live Collections · Middleware · Sessions │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
EmDash läuft als Astro-Integration. Es injiziert Routen für das Admin-Panel und REST-APIs, bietet einen Content-Loader für Live Collections und verwaltet Datenbank-Migrationen und Speicherverbindungen.
Database-First-Schema
Anders als traditionelle CMS, die das Schema im Code definieren, speichert EmDash Schema-Definitionen in der Datenbank selbst. Zwei Systemtabellen verfolgen Ihre Content-Struktur:
_emdash_collections— Collection-Metadaten (Slug, Label, Features)_emdash_fields— Felddefinitionen für jede Collection
Wenn Sie über die Admin-UI eine “products”-Collection mit title- und price-Feldern erstellen, führt EmDash Folgendes aus:
- Fügt Datensätze in
_emdash_collectionsund_emdash_fieldsein - Führt
ALTER TABLEaus, umec_productsmit den entsprechenden Spalten zu erstellen
Dieses Design ermöglicht:
- Laufzeit-Schema-Modifikation — Erstellen und Bearbeiten von Content-Typen ohne Code-Änderungen oder Rebuilds
- Entwicklerfreundliches Setup für Nicht-Entwickler — Content-Editoren können ihr Datenmodell über die UI gestalten
- Echte SQL-Spalten — Ordnungsgemäße Indizierung, Fremdschlüssel und Query-Optimierung
Tabellen pro Collection
Jede Collection erhält ihre eigene SQLite-Tabelle mit einem ec_-Präfix:
-- Erstellt, wenn "posts"-Collection hinzugefügt wird
CREATE TABLE ec_posts (
-- Systemspalten (immer vorhanden)
id TEXT PRIMARY KEY,
slug TEXT UNIQUE,
status TEXT DEFAULT 'draft', -- draft, published, scheduled
author_id TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
published_at TEXT,
deleted_at TEXT, -- Soft Delete
version INTEGER DEFAULT 1, -- Optimistic Locking
-- Content-Spalten (aus Ihren Felddefinitionen)
title TEXT NOT NULL,
content JSON, -- Portable Text
excerpt TEXT
);
Warum Tabellen pro Collection statt einer einzelnen Content-Tabelle mit JSON?
- Echte SQL-Spalten ermöglichen ordnungsgemäße Indizierung und Queries
- Fremdschlüssel funktionieren korrekt
- Schema ist selbstdokumentierend in der Datenbank
- Kein JSON-Parsing-Overhead für Feldzugriff
- Datenbanktools können Schema direkt inspizieren
Live Collections Integration
EmDash verwendet Astro 6’s Live Collections, um Content zur Laufzeit bereitzustellen. Content-Änderungen sind sofort verfügbar ohne statische Rebuilds.
emdashLoader() implementiert Astros LiveLoader-Interface:
// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
Abfragen von Content mit den bereitgestellten Wrapper-Funktionen:
import { getEmDashCollection, getEmDashEntry } from "emdash";
// Alle veröffentlichten Posts abrufen
const { entries: posts } = await getEmDashCollection("posts");
// Entwürfe abrufen
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});
// Einzelnen Eintrag per Slug abrufen
const { entry: post } = await getEmDashEntry("posts", "my-post-slug");
Routen-Injektion
Die EmDash-Integration verwendet Astros injectRoute-API, um Admin- und API-Routen hinzuzufügen:
| Pfadmuster | Zweck |
|---|---|
/_emdash/admin/[...path] | Admin-Panel SPA |
/_emdash/api/manifest | Admin-Manifest (Collections, Plugins) |
/_emdash/api/content/[collection] | CRUD für Content-Einträge |
/_emdash/api/media/* | Medienbibliothek-Operationen |
/_emdash/api/schema/* | Schema-Verwaltung |
/_emdash/api/settings | Site-Einstellungen |
/_emdash/api/menus/* | Navigationsmenüs |
/_emdash/api/taxonomies/* | Kategorien, Tags, benutzerdefinierte Taxonomien |
Routen werden aus dem emdash-Paket injiziert — nichts wird in Ihr Projekt kopiert.
Datenschicht
EmDash verwendet Kysely für typsichere SQL-Queries über alle unterstützten Datenbanken:
SQLite
Lokale Entwicklung mit sqlite({ url: "file:./data.db" })
D1
Cloudflares serverloses SQL mit d1({ binding: "DB" })
libSQL
Remote SQLite mit libsql({ url: "...", authToken: "..." })
Die Datenbankkonfiguration wird in astro.config.mjs an die Integration übergeben:
import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { sqlite } from "emdash/db";
import { local } from "emdash/storage";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
Speicher-Abstraktion
Mediendateien werden getrennt von der Datenbank gespeichert. EmDash unterstützt:
- Lokales Dateisystem — Entwicklung und einfache Deployments
- Cloudflare R2 — S3-kompatibler Object Storage am Edge
- S3-kompatibel — Jeder S3-kompatible Object Storage
Uploads verwenden signierte URLs für direkte Client-zu-Speicher-Uploads und umgehen Workers Body-Size-Limits.
Plugin-Architektur
Plugins erweitern EmDash durch ein von WordPress inspiriertes Hook-System:
- Content-Hooks —
content:beforeSave,content:afterSave,content:beforeDelete,content:afterDelete - Media-Hooks —
media:beforeUpload,media:afterUpload - Isolierter Speicher — Jedes Plugin erhält namespaced KV-Zugriff
- Admin-UI-Erweiterungen — Dashboard-Widgets, Einstellungsseiten, benutzerdefinierte Feld-Editoren
Plugins können in zwei Modi laufen:
- Native — Voller Zugriff auf die Host-Umgebung (für First-Party-Plugins)
- Sandboxed — Laufen in V8 Isolates mit capability-basierten Berechtigungen (für Third-Party-Plugins auf Cloudflare)
// astro.config.mjs
import { seoPlugin } from "@emdash-cms/plugin-seo";
emdash({
plugins: [seoPlugin({ maxTitleLength: 60 })],
});
Request-Flow
Eine typische Content-Anfrage folgt diesem Pfad:
- Astro empfängt Request — Ihre Page-Komponente läuft
- Query Content —
getEmDashCollection()ruft AstrosgetLiveCollection()auf - Loader führt aus —
emdashLoaderfragt die entsprechendeec_*-Tabelle via Kysely ab - Daten werden zurückgegeben — Einträge werden auf Astros Eintragsformat mit
id,slugunddatagemappt - Seite rendert — Ihre Komponente erhält den Content und rendert HTML
Für Admin-Requests:
- Middleware authentifiziert — Validiert Session-Token
- API-Route behandelt Request — CRUD-Operationen via Repositories
- Hooks feuern —
beforeCreate,afterUpdate, etc. - Datenbank-Updates — Kysely führt SQL aus
- Response wird zurückgegeben — JSON-Response an Admin-SPA
Virtuelle Module
EmDash generiert virtuelle Module zur Build-Zeit, um die Runtime zu konfigurieren:
| Modul | Zweck |
|---|---|
virtual:emdash/config | Datenbank- und Speicherkonfiguration |
virtual:emdash/dialect | Datenbank-Dialekt-Factory |
virtual:emdash/plugin-admins | Statische Imports für Plugin-Admin-UIs |
Dieser Ansatz stellt sicher, dass Bundler Plugin-Code ordnungsgemäß auflösen und tree-shaken können.
Nächste Schritte
Collections
Erfahren Sie mehr über Content Collections und Feldtypen.
Content Model
Verstehen Sie das Database-First Content Model.
Admin Panel
Erkunden Sie die Admin-Panel-Architektur.