Communitygithub.com

Padrões Strapi

CMS headless de código aberto — schemas de tipo de conteúdo, API REST, Document Service API, controladores personalizados, hooks de ciclo de vida, autenticação, TypeScript

O que é Padrões Strapi?

Este skill fornece um guia abrangente para usar o Strapi como um CMS headless de código aberto, com foco especial na versão 5 e na API Document Service. Ele cobre a definição de schemas de tipo de conteúdo, consulta à API REST com a biblioteca `qs`, uso da API Document Service para operações CRUD no backend, criação de controladores, serviços, rotas, políticas e middlewares personalizados, além de hooks de ciclo de vida e autenticação JWT. O skill enfatiza práticas recomendadas, como sempre passar `populate` para relações e mídia, usar `documentId` em vez de `id` de banco de dados e sanitizar/validar entradas e saídas em controladores personalizados. Ele é útil para desenvolvedores que constroem aplicações gerenciadas por conteúdo com Strapi, oferecendo decisões claras entre usar a API REST ou a Document Service API, estratégias de população, e um guia de solução de problemas. O skill suporta as plataformas Strapi v4 e v5, com ênfase nas mudanças da v5.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agents-inc/skills/tree/HEAD/src/skills/api-cms-strapi

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que Padrões Strapi faz?

Guia Rápido: Use o Strapi como um CMS headless de código aberto com APIs REST/GraphQL geradas automaticamente a partir de schemas de tipo de conteúdo. No v5, use a Document Service API (strapi.documents()) para acesso a dados no backend em vez do Entity Service (obsoleto). As respostas da API REST usam um formato plano (data.fieldName, não data.attributes.fieldName). Relações e mídia NÃO são populadas por padrão — sempre passe populate. Use qs para construir strings de consulta complexas. Os tipos de conteúdo são privados por padrão; configure permissões via plugin Usuários e Permissões ou tokens de API.


<critical_requirements>

CRÍTICO: Antes de Usar Este Skill

Todo código deve seguir as convenções do projeto em CLAUDE.md (kebab-case, exports nomeados, ordenação de imports, import type, constantes nomeadas)

(Você DEVE usar strapi.documents('api::content-type.content-type') (Document Service API) para todo acesso a dados no backend no Strapi v5 — a Entity Service API foi removida)

(Você DEVE sempre passar populate quando precisar de relações, mídia, componentes ou zonas dinâmicas — o Strapi retorna NENHUMA relação por padrão)

(Você DEVE usar a biblioteca qs para construir strings de consulta complexas da API REST com filtros, populate e sort — a construção manual de strings quebra com parâmetros aninhados)

(Você DEVE sanitizar e validar tanto a entrada quanto a saída em controladores personalizados usando this.sanitizeQuery(ctx), this.sanitizeOutput() e this.validateQuery(ctx))

(Você DEVE definir permissões para cada endpoint de tipo de conteúdo via painel de administração ou configuração — todas as rotas são privadas por padrão)

</critical_requirements>


Detecção automática: Strapi, strapi, @strapi/strapi, createCoreController, createCoreService, createCoreRouter, Document Service, strapi.documents, content-type, schema.json, api::, plugin::, lifecycle hooks, Users & Permissions, /api/auth/local, populate, qs.stringify

Quando usar:

  • Construindo aplicações gerenciadas por conteúdo com Strapi como CMS headless
  • Definindo schemas de tipo de conteúdo (schema.json) com campos, relações, componentes e zonas dinâmicas
  • Consultando a API REST com filtros, populate, sort e paginação
  • Criando controladores, serviços, rotas, políticas ou middlewares personalizados
  • Usando a Document Service API para CRUD no backend com fluxos de trabalho de rascunho/publicação
  • Implementando autenticação JWT com o plugin Usuários e Permissões
  • Adicionando hooks de ciclo de vida a tipos de conteúdo para efeitos colaterais
  • Gerando tipos TypeScript para schemas de conteúdo

Padrões principais cobertos:

  • Definição de schema de tipo de conteúdo (schema.json)
  • Consulta à API REST com qs (filtros, populate, sort, paginação)
  • Document Service API (findMany, findOne, create, update, delete, publish, unpublish)
  • Controladores, serviços, rotas, políticas e middlewares personalizados
  • Hooks de ciclo de vida (beforeCreate, afterUpdate, etc.)
  • Autenticação JWT (registro, login, requisições autenticadas)
  • Geração de tipos TypeScript

Quando NÃO usar:

  • Plataformas CMS que não sejam Strapi (use o skill dedicado para seu CMS)
  • Consultas diretas ao banco de dados ignorando a camada de API do Strapi (use o Document Service do Strapi)
  • Lógica transacional complexa que exija SQL puro (o Strapi abstrai o banco de dados)

Recursos Detalhados:

  • Para frameworks de decisão e tabelas de referência rápida, veja reference.md

API Core & REST:

  • examples/core.md — Schemas de tipo de conteúdo, consulta à API REST, Document Service API, tratamento de erros

Personalização do Backend:

  • examples/backend.md — Controladores, serviços, rotas, políticas, middlewares personalizados, hooks de ciclo de vida, middleware do Document Service

Autenticação:

  • examples/auth.md — Autenticação JWT, registro, login, papéis e permissões

Filosofia

Strapi é um CMS headless de código aberto construído em Node.js (Koa) que gera automaticamente APIs RESTful e GraphQL a partir de schemas de tipo de conteúdo. O conteúdo é definido via schemas JSON, gerenciado através de um painel de administração e consumido via endpoints de API gerados.

Princípios principais:

  1. Conteúdo orientado por schema — Os tipos de conteúdo são definidos em arquivos schema.json que descrevem campos, relações, componentes e zonas dinâmicas. O Construtor de Tipo de Conteúdo do painel de administração fornece um editor visual, mas os schemas são código que vive em seu repositório.
  2. APIs geradas automaticamente — Cada tipo de conteúdo obtém automaticamente endpoints CRUD REST (/api/:pluralApiId) e suporte GraphQL opcional. Nenhuma criação manual de rota/controlador necessária para operações padrão.
  3. Document Service API (v5) — A API de backend para acessar conteúdo de código personalizado, plugins e hooks de ciclo de vida. Substitui o Entity Service do v4. Usa documentId (não id de banco de dados) como identificador primário.
  4. Rascunho e Publicar — Os tipos de conteúdo podem ter fluxos de trabalho de rascunho/publicação. O Document Service usa como padrão status: 'draft'; conteúdo publicado requer status: 'published' ou chamadas explícitas de publish().
  5. Permissão primeiro — Todos os endpoints de tipo de conteúdo são privados por padrão. O acesso deve ser explicitamente concedido via painel de administração (plugin Usuários e Permissões) ou tokens de API.
  6. Personalização do backend — Controladores, serviços, rotas, políticas e middlewares podem ser personalizados. O Strapi segue um padrão semelhante ao MVC construído sobre o Koa.

Padrões Principais

Padrão 1: Schema de Tipo de Conteúdo

Tipos de conteúdo são definidos em arquivos schema.json em ./src/api/[api-name]/content-types/[content-type-name]/schema.json. Use collectionType para conteúdo de múltiplos documentos (artigos, produtos) e singleType para conteúdo de documento único (configurações do site, página inicial).

{
  "kind": "collectionType",
  "info": {
    "singularName": "article",
    "pluralName": "articles",
    "displayName": "Article"
  },
  "options": { "draftAndPublish": true },
  "attributes": {
    "title": { "type": "string", "required": true },
    "slug": { "type": "uid", "targetField": "title" },
    "author": {
      "type": "relation",
      "relation": "manyToOne",
      "target": "api::author.author",
      "inversedBy": "articles"
    },
    "blocks": {
      "type": "dynamiczone",
      "components": ["blocks.hero", "blocks.rich-text"]
    }
  }
}

Campos chave: uid gera automaticamente slugs a partir de targetField, tipos relation definem cardinalidade com inversedBy/mappedBy, component incorpora blocos reutilizáveis, dynamiczone permite tipos de componente mistos. Veja examples/core.md para exemplos completos de coleção e tipo único.


Padrão 2: Consulta à API REST com qs

A API REST aceita parâmetros de consulta complexos para filtragem, população, ordenação e paginação. Sempre use a biblioteca qs para construir strings de consulta.

import qs from "qs";

const query = qs.stringify(
  {
    filters: { publishedAt: { $notNull: true } },
    populate: {
      author: { fields: ["name"] },
      categories: { fields: ["name", "slug"] },
    },
    sort: ["publishedAt:desc"],
    pagination: { page: 1, pageSize: 25 },
  },
  { encodeValuesOnly: true },
);
const response = await fetch(`${STRAPI_URL}/api/articles?${query}`);
const { data, meta } = await response.json();

Pontos chave: encodeValuesOnly previne problemas de codificação de colchetes, filtros usam operadores ($eq, $notNull, $containsi, $or, $and), populate direcionado com fields evita busca excessiva (nunca use populate=* em produção), componentes de zona dinâmica usam sintaxe on. Veja examples/core.md para exemplos completos de filtragem, população e paginação.


Padrão 3: Document Service API (Backend)

A Document Service API é usada em controladores personalizados, serviços, hooks de ciclo de vida e plugins para acessar conteúdo do lado do servidor. Ela substitui o Entity Service do v4 (removido no v5).

const CONTENT_TYPE_UID = "api::article.article";

// Encontrar conteúdo publicado (padrão é 'draft')
const articles = await strapi.documents(CONTENT_TYPE_UID).findMany({
  status: "published",
  filters: { categories: { slug: { $eq: "news" } } },
  populate: { author: true },
});

// CRUD usa documentId (não id de banco de dados)
const article = await strapi
  .documents(CONTENT_TYPE_UID)
  .findOne({ documentId });
const created = await strapi
  .documents(CONTENT_TYPE_UID)
  .create({ data: { title: "Novo" } });
await strapi.documents(CONTENT_TYPE_UID).publish({ documentId });

Pontos chave: strapi.documents(uid) substitui strapi.entityService (removido no v5), documentId é o identificador persistente entre localidades e versões de rascunho/publicadas, o status padrão é 'draft' (passe status: 'published' explicitamente), métodos dedicados publish()/unpublish()/discardDraft(). Veja examples/core.md para exemplos completos de CRUD e publicar/cancelar publicação.


Padrão 4: Controladores Personalizados

Estenda ou substituia as ações do controlador geradas automaticamente. Controladores lidam com a lógica de requisição/resposta e delegam a serviços. Use createCoreController de factories para herdar helpers de sanitização.

// src/api/article/controllers/article.ts
export default factories.createCoreController(
  "api::article.article",
  ({ strapi }) => ({
    async find(ctx) {
      await this.validateQuery(ctx);
      const sanitizedQuery = await this.sanitizeQuery(ctx);
      const { results, pagination } = await strapi
        .service("api::article.article")
        .find(sanitizedQuery);
      const sanitizedResults = await this.sanitizeOutput(results, ctx);
      return this.transformResponse(sanitizedResults, { pagination });
    },
  }),
);

Por que é bom: validateQuery + sanitizeQuery + sanitizeOutput aplicam acesso a campos baseado em papéis, transformResponse() envolve a saída em envelope { data, meta }

Veja examples/backend.md para exemplos completos de controladores incluindo ações personalizadas (findBySlug, incrementViews, search).


Padrão 5: Rotas Personalizadas

Defina rotas personalizadas para expor ações de controlador personalizadas. Arquivos de rota personalizada ficam ao lado do roteador principal.

// src/api/article/routes/custom-article.ts
export default {
  routes: [
    {
      method: "GET",
      path: "/articles/slug/:slug",
      handler: "api::article.article.findBySlug",
      config: { auth: false },
    },
  ],
};

Por que é bom: Arquivo separado do roteador principal, handler usa UID completo, auth: false para acesso público, políticas/middlewares anexáveis por rota

Veja examples/backend.md para restrição de rota com only, anexação de middleware e configuração de política.


Padrão 6: Hooks de Ciclo de Vida

Registre efeitos colaterais em operações de tipo de conteúdo. Hooks são definidos em arquivos lifecycles.ts junto ao schema do tipo de conteúdo.

// src/api/article/content-types/article/lifecycles.ts
export default {
  async beforeCreate(event) {
    /* event.params.data — modifica para transformar entrada */
  },
  async afterCreate(event) {
    /* event.result — o documento criado */
  },
  async beforeUpdate(event) {
    /* event.params.data — modifica antes de salvar */
  },
  async afterDelete(event) {
    /* event.result — limpa recursos relacionados */
  },
};

Por que é bom: Hooks disparam automaticamente em operações do Document Service, beforeXxx pode modificar event.params.data, afterXxx tem event.result

Nota: Para preocupações transversais no v5 (logging de auditoria, invalidação de cache), prefira middleware do Document Service (strapi.documents.use((ctx, next) => { ... })) registrado em src/index.ts. Hooks de ciclo de vida ainda estão disponíveis para efeitos colaterais específicos de tipo de conteúdo.

Veja examples/backend.md para exemplos completos de ciclo de vida incluindo geração de slug, inscrição programática e logging de auditoria.


Padrão 7: Serviços

Serviços contêm lógica de negócios reutilizável chamada por controladores. Use createCoreService para herdar CRUD padrão e adicionar métodos personalizados.

export default factories.createCoreService(
  "api::article.article",
  ({ strapi }) => ({
    async findPublished(filters = {}) {
      return strapi
        .documents("api::article.article")
        .findMany({ status: "published", filters });
    },
  }),
);
// Chamado via: strapi.service("api::article.article").findPublished()

Por que é bom: Herda CRUD padrão, métodos personalizados encapsulam lógica de consulta reutilizável, chamados de controladores via strapi.service(uid)

Veja examples/backend.md para exemplos complexos de serviço incluindo consultas de artigos relacionados e padrões de publicar com notificação.


Padrão 8: Políticas

Políticas são funções somente leitura que permitem ou negam acesso a uma rota. Elas retornam true (permitir) ou false (negar) e não podem modificar a requisição.

// src/api/article/policies/is-owner.ts
export default async (policyContext, config, { strapi }) => {
  const article = await strapi.documents("api::article.article").findOne({
    documentId: policyContext.params.documentId,
    populate: { author: true },
  });
  return article?.author?.documentId === policyContext.state.user?.documentId;
};

Por que é bom: Verificação somente leitura (sem mutação de requisição), aplicada por ação na configuração do roteador principal

Veja examples/backend.md para aplicação de políticas a rotas, políticas de limitação de taxa e encadeamento de múltiplas políticas.


<decision_framework>

Framework de Decisão

API REST vs Document Service API

Onde seu código está sendo executado?
+-- Lado do cliente (navegador, aplicativo frontend)
|   +-- Use API REST (fetch /api/:pluralApiId)
+-- Lado do servidor (controlador personalizado, serviço, plugin, hook de ciclo de vida)
    +-- Use Document Service API (strapi.documents(uid))

Estratégia de População

Você precisa de dados relacionados?
+-- NÃO --> Não passe populate (resposta mais enxuta)
+-- SIM --> Qual nível de controle?
    +-- Todas as relações, 1 nível --> populate: '*' (conveniente, mas busca em excesso)
    +-- Relações específicas --> populate: { relation: { fields: [...] } }
    +-- Relações aninhadas

…

[cortado para tradução]

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

Habilidades Relacionadas