EmDash integra-se profundamente com Astro para fornecer uma experiência CMS completa. Esta página explica as principais decisões arquiteturais e como as peças se encaixam.
Visão geral de alto nível
┌──────────────────────────────────────────────────────────────────┐
│ Seu site Astro │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Integração EmDash │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ Conteúdo │ │ Admin │ │ Plugins │ │ │
│ │ │ APIs │ │ Painel │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └───────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Camada de dados │ │ │
│ │ │ Banco de dados (D1/SQLite) + Storage (R2/S3) │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Framework Astro │ │
│ │ Live Collections · Middleware · Sessões │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
EmDash funciona como uma integração Astro. Ele injeta rotas para o painel de administração e APIs REST, fornece um carregador de conteúdo para Live Collections e gerencia migrações de banco de dados e conexões de armazenamento.
Esquema database-first
Ao contrário dos CMS tradicionais que definem o esquema no código, EmDash armazena as definições do esquema no próprio banco de dados. Duas tabelas do sistema rastreiam a estrutura do seu conteúdo:
_emdash_collections— Metadados da coleção (slug, rótulo, recursos)_emdash_fields— Definições de campo para cada coleção
Quando você cria uma coleção “products” com campos title e price através da interface de administração, EmDash:
- Insere registros em
_emdash_collectionse_emdash_fields - Executa
ALTER TABLEpara criarec_productscom as colunas apropriadas
Este design permite:
- Modificação do esquema em tempo de execução — Criar e editar tipos de conteúdo sem alterações de código ou reconstruções
- Configuração amigável para não desenvolvedores — Editores de conteúdo podem projetar seu modelo de dados através da interface
- Colunas SQL reais — Indexação adequada, chaves estrangeiras e otimização de consultas
Tabelas por coleção
Cada coleção obtém sua própria tabela SQLite com prefixo ec_:
-- Criada quando a coleção "posts" é adicionada
CREATE TABLE ec_posts (
-- Colunas do sistema (sempre presentes)
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, -- Exclusão suave
version INTEGER DEFAULT 1, -- Bloqueio otimista
-- Colunas de conteúdo (de suas definições de campo)
title TEXT NOT NULL,
content JSON, -- Portable Text
excerpt TEXT
);
Por que tabelas por coleção em vez de uma única tabela de conteúdo com JSON?
- Colunas SQL reais permitem indexação e consultas adequadas
- Chaves estrangeiras funcionam corretamente
- Esquema é autodocumentado no banco de dados
- Sem sobrecarga de análise JSON para acesso a campos
- Ferramentas de banco de dados podem inspecionar o esquema diretamente
Integração de Live Collections
EmDash usa as Live Collections do Astro 6 para servir conteúdo em tempo de execução. Alterações de conteúdo estão imediatamente disponíveis sem reconstruções estáticas.
emdashLoader() implementa a interface LiveLoader do Astro:
// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
Consulte conteúdo usando as funções wrapper fornecidas:
import { getEmDashCollection, getEmDashEntry } from "emdash";
// Obter todos os posts publicados
const { entries: posts } = await getEmDashCollection("posts");
// Obter rascunhos
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});
// Obter uma única entrada por slug
const { entry: post } = await getEmDashEntry("posts", "my-post-slug");
Injeção de rotas
A integração EmDash usa a API injectRoute do Astro para adicionar rotas de administração e API:
| Padrão de caminho | Propósito |
|---|---|
/_emdash/admin/[...path] | SPA do painel de administração |
/_emdash/api/manifest | Manifesto admin (coleções, plugins) |
/_emdash/api/content/[collection] | CRUD para entradas de conteúdo |
/_emdash/api/media/* | Operações de biblioteca de mídia |
/_emdash/api/schema/* | Gerenciamento de esquema |
/_emdash/api/settings | Configurações do site |
/_emdash/api/menus/* | Menus de navegação |
/_emdash/api/taxonomies/* | Categorias, tags, taxonomias personalizadas |
As rotas são injetadas do pacote emdash — nada é copiado para o seu projeto.
Camada de dados
EmDash usa Kysely para consultas SQL com segurança de tipo em todos os bancos de dados suportados:
SQLite
Desenvolvimento local com sqlite({ url: "file:./data.db" })
D1
SQL serverless da Cloudflare com d1({ binding: "DB" })
libSQL
SQLite remoto com libsql({ url: "...", authToken: "..." })
A configuração do banco de dados é passada para a integração em astro.config.mjs:
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",
}),
}),
],
});
Abstração de armazenamento
Arquivos de mídia são armazenados separadamente do banco de dados. EmDash suporta:
- Sistema de arquivos local — Desenvolvimento e implantações simples
- Cloudflare R2 — Armazenamento de objetos compatível com S3 no edge
- Compatível com S3 — Qualquer armazenamento de objetos compatível com S3
Os uploads usam URLs assinadas para uploads diretos de cliente para armazenamento, ignorando os limites de tamanho de corpo dos Workers.
Arquitetura de plugins
Plugins estendem EmDash através de um sistema de hooks inspirado no WordPress:
- Hooks de conteúdo —
content:beforeSave,content:afterSave,content:beforeDelete,content:afterDelete - Hooks de mídia —
media:beforeUpload,media:afterUpload - Armazenamento isolado — Cada plugin obtém acesso KV com namespace
- Extensões de UI de administração — Widgets de painel, páginas de configurações, editores de campo personalizados
Plugins podem funcionar em dois modos:
- Nativo — Acesso completo ao ambiente host (para plugins próprios)
- Sandboxed — Executar em isolados V8 com permissões baseadas em capacidade (para plugins de terceiros no Cloudflare)
// astro.config.mjs
import { seoPlugin } from "@emdash-cms/plugin-seo";
emdash({
plugins: [seoPlugin({ maxTitleLength: 60 })],
});
Fluxo de solicitação
Uma solicitação de conteúdo típica segue este caminho:
- Astro recebe a solicitação — Seu componente de página é executado
- Consultar conteúdo —
getEmDashCollection()chamagetLiveCollection()do Astro - O carregador executa —
emdashLoaderconsulta a tabelaec_*apropriada via Kysely - Dados retornados — Entradas são mapeadas para o formato de entrada do Astro com
id,slugedata - A página renderiza — Seu componente recebe o conteúdo e renderiza HTML
Para solicitações de administração:
- Middleware autentica — Valida token de sessão
- Rota da API manipula a solicitação — Operações CRUD via repositórios
- Hooks são disparados —
beforeCreate,afterUpdate, etc. - Atualizações do banco de dados — Kysely executa SQL
- Resposta retornada — Resposta JSON para a SPA de administração
Módulos virtuais
EmDash gera módulos virtuais no momento da compilação para configurar o runtime:
| Módulo | Propósito |
|---|---|
virtual:emdash/config | Configuração de banco de dados e armazenamento |
virtual:emdash/dialect | Factory de dialeto de banco de dados |
virtual:emdash/plugin-admins | Importações estáticas para UI admin de plugins |
Esta abordagem garante que os bundlers possam resolver e fazer tree-shake do código de plugins corretamente.
Próximos passos
Coleções
Aprenda sobre coleções de conteúdo e tipos de campo.
Modelo de conteúdo
Entenda o modelo de conteúdo database-first.
Painel de administração
Explore a arquitetura do painel de administração.