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 para Cloudflare, publica um post e acompanha esse post pelo código do template.

Pré-requisitos

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

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

Criar o scaffold do blog

O comando a seguir cria my-blog a partir do template de blog para Cloudflare e instala as dependências com o 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 informar que a instalação das 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 e depois abra /_emdash/admin. Conclua a tela de configuração se esta for a primeira execução. Os dados seed do template criam o modelo de conteúdo e o 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 por meio de supports, e habilita comentários separadamente com commentsEnabled: true;
  • pages oferece suporte a rascunhos, revisões e busca.

Cada post tem estes campos personalizados:

CampoFinalidade
titleTítulo obrigatório do post
featured_imageImagem opcional exibida com o post
contentCorpo em Portable Text
excerptTexto curto usado nas listas de posts e como alternativa para os metadados

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

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

Publicar o primeiro post

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

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

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

  4. Selecione uma imagem destacada da biblioteca de mídia ou envie uma. Adicione um texto alternativo que descreva a função da imagem no post.

  5. Atribua um byline, uma categoria e as tags relevantes no painel de configurações.

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

  7. Selecione Preview e confira a página do post. Volte 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 ele não aparecer, confirme que o editor mostra Published, e não Draft nem Scheduled.

Depois da publicação, as edições são salvas automaticamente em um novo rascunho enquanto o post atual permanece no ar. Selecione Publish changes quando o rascunho revisado deve substituí-lo. O guia de criação de conteúdo explica pré-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 no 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 as entradas publicadas por padrão. Ela usa published_at, o nome do campo no banco de dados, para ordenar. A propriedade publishedAt retornada é um Date do JavaScript para renderização.

O helper de taxonomias recebe post.data.id porque as atribuições de taxonomia pertencem ao ID de conteúdo estável. Os links usam post.id, pois esse é o slug voltado para a 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 agrupa as buscas de tags com getTermsForEntries() em vez de consultar uma vez para cada post. Os bylines já estão incluídos em post.data.bylines pela consulta de coleção.

Seguir a consulta de post

A rota dinâmica do post lê o slug da URL e chama getEmDashEntry(). O trecho a seguir mostra o caminho essencial de consulta e renderização, enquanto o template completo também trata SEO, bylines, 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.rewrite("/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.rewrite("/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>

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

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

Usar categorias e tags

O template inclui rotas de arquivo para cada categoria e tag. A rota de uma categoria primeiro resolve o slug do termo e depois filtra os 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.rewrite("/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 }. Os editores gerenciam termos e atribuições no administrador; o guia de taxonomias cobre categorias hierárquicas, tags planas e taxonomias personalizadas.

getEmDashEntry() inclui os termos atribuídos ao post, de modo que 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 Older posts. Mantenha orderBy: { published_at: "desc" } inalterado entre as páginas para que a ordem das entradas não mude inesperadamente.

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

Verificar o feed RSS

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

Depois de publicar o post de teste, abra /rss.xml e procure pelo título dele. Se o site for usar uma URL absoluta de produção nos feeds, defina a opção site do Astro antes do deploy; durante o desenvolvimento local, o endpoint recorre à origem da requisição atual.

Neste ponto, o blog tem um fluxo de criação, páginas de post em tempo de execução, arquivos de taxonomias, renderização de mídia e um feed. Continue com Consultar conteúdo para filtros e paginação, ou com Biblioteca de mídia para a edição de recursos e o rastreamento de uso. Para redigir ou editar posts com um assistente de IA, siga Ferramentas de IA.