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ãodata.attributes.fieldName). Relações e mídia NÃO são populadas por padrão — sempre passepopulate. Useqspara 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:
- Conteúdo orientado por schema — Os tipos de conteúdo são definidos em arquivos
schema.jsonque 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. - 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. - 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ãoidde banco de dados) como identificador primário. - 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 requerstatus: 'published'ou chamadas explícitas depublish(). - 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.
- 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]