Criar um Blog

Nesta página

O template de blog do EmDash fornece um site Astro funcional com posts, páginas, autores, categorias, tags, busca, comentários, widgets e um feed RSS. Este tutorial cria a versão Cloudflare, publica um post e rastreia esse post através do código do template.

Pré-requisitos

Instale o Node.js 22.12 ou posterior e o pnpm antes de começar.

Você precisa de uma conta Cloudflare apenas quando for implantar o site. Durante o desenvolvimento local, o template executa versões locais do banco de dados e armazenamento de arquivos no seu computador.

Criar o scaffold do blog

O seguinte comando cria my-blog a partir do template de blog Cloudflare e instala suas dependências com pnpm:

npm create emdash@latest my-blog -- --template cloudflare:blog --pm pnpm --yes

O scaffolder também cria um arquivo .env local com uma EMDASH_ENCRYPTION_KEY. O .gitignore gerado exclui o .env do controle de versão. Se o comando reportar que a instalação de dependências falhou, entre no diretório do projeto e execute pnpm install antes de continuar.

Inicie o servidor de desenvolvimento local:

cd my-blog
pnpm dev

Abra a URL local exibida no terminal, depois abra /_emdash/admin. Complete a tela de configuração se esta for a primeira execução. Os dados seed do template criam o modelo de conteúdo e conteúdo de exemplo durante a configuração.

Entender o modelo de conteúdo

O template define duas coleções em seed/seed.json:

  • posts habilita rascunhos, revisões, busca e SEO através de supports, e habilita comentários separadamente com commentsEnabled: true;
  • pages suporta rascunhos, revisões e busca.

Cada post tem estes campos personalizados:

CampoPropósito
titleTítulo obrigatório do post
featured_imageImagem opcional exibida com o post
contentCorpo em Portable Text
excerptTexto curto usado em listas de posts e fallbacks de metadados

O EmDash adiciona campos de sistema como o ID de conteúdo estável, slug, status, horários de criação e atualização e horário de publicação. O template também define taxonomias category e tag para posts, além de linhas de autoria que podem creditar um ou mais autores.

O servidor de desenvolvimento gera emdash-env.d.ts a partir deste esquema. Como resultado, getEmDashCollection("posts") retorna entradas cuja propriedade data é tipada como Post.

Publicar o primeiro post

  1. Na barra lateral do admin, selecione Posts, depois Add New.

  2. Digite um título. O EmDash sugere um slug a partir do título; edite-o se a URL pública precisar de um valor diferente.

  3. Adicione um resumo e escreva o corpo no editor Content.

  4. Selecione uma imagem destacada da Biblioteca de Mídia ou faça upload de uma. Adicione texto alternativo que descreva o propósito da imagem no post.

  5. Atribua uma linha de autoria, categoria e quaisquer tags relevantes no painel de configurações.

  6. Selecione Save. A entrada se torna um rascunho e o editor abre sua URL de entrada permanente.

  7. Selecione Preview e verifique a página do post. Retorne ao editor e selecione Publish quando o rascunho estiver pronto.

Abra /posts/your-post-slug no site local. O post também aparece na página inicial e no arquivo de posts. Se não aparecer, confirme que o editor mostra Published, em vez de Draft ou Scheduled.

Após a publicação, as edições são salvas automaticamente em um novo rascunho enquanto o post atual permanece ativo. Selecione Publish changes quando o rascunho revisado deve substituí-lo. O guia de criação de conteúdo explica visualizações, agendamento, revisões e bloqueios de edição.

Seguir a consulta de coleção

A página inicial e o arquivo de posts chamam getEmDashCollection() durante a renderização do servidor. O template ordena os posts no banco de dados pelo campo armazenado published_at:

---
import { getEmDashCollection, getTermsForEntries } from "emdash";

const { entries: posts, cacheHint } = await getEmDashCollection("posts", {
  orderBy: { published_at: "desc" },
});

if (Astro.cache?.enabled) Astro.cache.set(cacheHint);

const tagsByEntry = await getTermsForEntries(
  "posts",
  posts.map((post) => post.data.id),
  "tag",
);
---

A consulta de coleção retorna entradas publicadas por padrão. Ela usa published_at, o nome do campo no banco de dados, para ordenação. A propriedade publishedAt retornada é um JavaScript Date para renderização.

O helper de taxonomia recebe post.data.id porque as atribuições de taxonomia pertencem ao ID de conteúdo estável. Links usam post.id em vez disso, porque esse é o slug voltado para URL produzido pelo carregador de conteúdo:

<a href={`/posts/${post.id}`}>
  <h2>{post.data.title}</h2>
  {post.data.excerpt && <p>{post.data.excerpt}</p>}
</a>

O template atual faz as consultas de tags em lote com getTermsForEntries() em vez de consultar uma vez para cada post. As linhas de autoria já estão incluídas em post.data.bylines pela consulta de coleção.

Seguir a consulta de post

A rota dinâmica de post lê o slug da URL e chama getEmDashEntry(). O trecho a seguir mostra a consulta essencial e o caminho de renderização enquanto o template completo também lida com SEO, linhas de autoria, comentários, posts relacionados e widgets:

---
import { decodeSlug, getEmDashEntry } from "emdash";
import { Image, PortableText } from "emdash/ui";

const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.redirect("/404");

const { entry: post, error, cacheHint } = await getEmDashEntry("posts", slug);

if (error) return new Response("Unable to load post", { status: 500 });
if (!post) return Astro.redirect("/404");
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---

<article>
  {post.data.featured_image && <Image image={post.data.featured_image} priority />}
  <h1>{post.data.title}</h1>
  <PortableText value={post.data.content} />
</article>

Image lê o valor de mídia selecionado pelo editor e gera saída responsiva. PortableText transforma os dados de bloco armazenados em títulos, parágrafos, links, imagens, blocos de código e os outros tipos de bloco suportados.

Ambos os templates de blog definem output: "server" em astro.config.mjs. Essas consultas são executadas quando uma requisição é renderizada, então o conteúdo publicado não depende de uma lista de rotas estáticas criada durante o build.

Usar categorias e tags

O template inclui rotas de arquivo para cada categoria e tag. Uma rota de categoria primeiro resolve o slug do termo, depois filtra posts por essa taxonomia:

---
import { decodeSlug, getEmDashCollection, getTerm } from "emdash";

const slug = decodeSlug(Astro.params.slug);
const category = slug
  ? await getTerm("category", slug, { includeCounts: false })
  : null;

if (!category) return Astro.redirect("/404");

const { entries: posts, error } = await getEmDashCollection("posts", {
  where: { category: category.slug },
  orderBy: { published_at: "desc" },
});

if (error) return new Response("Unable to load posts", { status: 500 });
---

A rota tag usa o mesmo padrão com getTerm("tag", slug) e where: { tag: term.slug }. Editores gerenciam termos e atribuições no admin; o guia de Taxonomias cobre categorias hierárquicas, tags planas e taxonomias personalizadas.

getEmDashEntry() inclui os termos atribuídos ao post, então a rota de detalhe pode renderizá-los sem outra consulta:

---
const categories = post.data.terms?.category ?? [];
const tags = post.data.terms?.tag ?? [];
---

{categories.map((category) => (
  <a href={`/category/${category.slug}`}>{category.label}</a>
))}
{tags.map((tag) => (
  <a href={`/tag/${tag.slug}`}>{tag.label}</a>
))}

Adicionar paginação ao arquivo

O arquivo de posts do template renderiza todos os posts publicados. Quando o arquivo crescer, adicione um limit e use paginação por offset para rotas numeradas como /posts/page/2, ou paginação por cursor para um link Posts mais antigos. Mantenha orderBy: { published_at: "desc" } inalterado entre páginas para que as entradas não mudem de ordem inesperadamente.

Os exemplos de paginação mostram ambas as abordagens e explicam quando escolher cada uma.

Verificar o feed RSS

O template já serve /rss.xml. Seu endpoint lê os 20 posts mais recentes com getEmDashCollection(), formata cada data de publicação e escapa o título e resumo antes de inseri-los no XML. Ele também lê o título do site e o tagline das configurações do EmDash.

Após publicar o post de teste, abra /rss.xml e procure pelo seu título. Se o site usar uma URL de produção absoluta nos feeds, defina a opção site do Astro antes da implantação; o endpoint volta ao origin da requisição atual durante o desenvolvimento local.

Neste ponto, o blog tem um fluxo de trabalho de autoria, páginas de posts em tempo de execução, arquivos de taxonomia, renderização de mídia e um feed. Continue com Consultando Conteúdo para filtros e paginação, ou Biblioteca de Mídia para edição de ativos e rastreamento de uso. Para rascunhar ou editar posts com um assistente de IA, siga Ferramentas de IA.