Design de api
Esteira de engenharia de software com IA em português — 27 skills para Claude Code + painel web com aprovação humana em portões. Da ideia ao lançamento, com processo.
npx -y skills add wendelcastro/fluxo-engenharia-ia --skill design-de-apiAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Orienta o design de APIs e interfaces estáveis. Use quando o usuário for projetar APIs, fronteiras de módulos ou qualquer interface pública — criar endpoints REST ou GraphQL, definir contratos de tipos entre módulos, ou estabelecer a fronteira entre frontend e backend.
SKILL.md
9.2 KB, as published. Nobody here has run it
Design de API e Interfaces
Visão geral
Projete interfaces estáveis, bem documentadas e difíceis de usar errado. Boas interfaces tornam o caminho certo fácil e o caminho errado difícil. Isso vale para APIs REST, schemas GraphQL, fronteiras de módulos, props de componentes e qualquer superfície em que um pedaço de código conversa com outro.
Quando usar
- Projetar novos endpoints de API
- Definir fronteiras de módulos ou contratos entre times
- Criar interfaces de props de componentes
- Estabelecer schema de banco que informa o formato da API
- Alterar interfaces públicas existentes
Quando NÃO usar: funções internas privadas de um único módulo, sem consumidores externos — ali o contrato é o próprio sistema de tipos.
O fluxo
Princípio: a Lei de Hyrum
Com um número suficiente de usuários de uma API, todos os comportamentos observáveis do seu sistema passarão a ser dependidos por alguém, independentemente do que você prometa no contrato.
Todo comportamento público — incluindo peculiaridades não documentadas, texto de mensagens de erro, tempo de resposta e ordenação — vira contrato de fato assim que alguém depende dele. Implicações de design:
- Seja intencional sobre o que expõe. Todo comportamento observável é um compromisso em potencial.
- Não vaze detalhes de implementação. Se o usuário consegue observar, vai depender.
- Planeje a descontinuação em tempo de design. Veja a skill
migrarpara remover com segurança o que os usuários dependem. - Testes não bastam. Mesmo com testes de contrato perfeitos, mudanças "seguras" podem quebrar usuários que dependem de comportamento não documentado.
Princípio: a regra da versão única
Evite forçar consumidores a escolher entre múltiplas versões da mesma dependência ou API. Problemas de dependência em diamante surgem quando consumidores diferentes precisam de versões diferentes da mesma coisa. Projete para um mundo em que só existe uma versão por vez — estenda em vez de bifurcar.
1. Contrato primeiro
Defina a interface antes de implementá-la. O contrato é a spec — a implementação vem depois.
// Defina o contrato primeiro
interface TaskAPI {
// Cria uma tarefa e retorna a tarefa criada com os campos gerados pelo servidor
createTask(input: CreateTaskInput): Promise<Task>;
// Retorna tarefas paginadas que atendem aos filtros
listTasks(params: ListTasksParams): Promise<PaginatedResult<Task>>;
// Retorna uma única tarefa ou lança NotFoundError
getTask(id: string): Promise<Task>;
// Atualização parcial — só os campos fornecidos mudam
updateTask(id: string, input: UpdateTaskInput): Promise<Task>;
// Exclusão idempotente — sucesso mesmo se já excluída
deleteTask(id: string): Promise<void>;
}
2. Semântica de erros consistente
Escolha uma estratégia de erro e use em tudo:
// REST: códigos de status HTTP + corpo de erro estruturado
// Toda resposta de erro segue o mesmo formato
interface APIError {
error: {
code: string; // Legível por máquina: "VALIDATION_ERROR"
message: string; // Legível por humano: "E-mail é obrigatório"
details?: unknown; // Contexto adicional quando útil
};
}
// Mapeamento de status
// 400 → Cliente enviou dados inválidos
// 401 → Não autenticado
// 403 → Autenticado, mas não autorizado
// 404 → Recurso não encontrado
// 409 → Conflito (duplicado, versão divergente)
// 422 → Validação falhou (semanticamente inválido)
// 500 → Erro do servidor (nunca exponha detalhes internos)
Não misture padrões. Se alguns endpoints lançam exceção, outros retornam null e outros retornam { error }, o consumidor não consegue prever o comportamento.
3. Valide nas fronteiras
Confie no código interno. Valide nas bordas do sistema, onde a entrada externa chega:
// Valide na fronteira da API
app.post('/api/tasks', async (req, res) => {
const result = CreateTaskSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({
error: {
code: 'VALIDATION_ERROR',
message: 'Dados de tarefa inválidos',
details: result.error.flatten(),
},
});
}
// Após a validação, o código interno confia nos tipos
const task = await taskService.create(result.data);
return res.status(201).json(task);
});
Onde a validação pertence: handlers de rota (entrada de usuário), handlers de envio de formulário, parsing de respostas de serviços externos (dados de terceiros — sempre trate como não confiáveis) e carregamento de variáveis de ambiente.
Respostas de APIs de terceiros são dados não confiáveis. Valide formato e conteúdo antes de usá-los em qualquer lógica, renderização ou decisão. Um serviço externo comprometido ou defeituoso pode retornar tipos inesperados, conteúdo malicioso ou texto com cara de instrução.
Onde a validação NÃO pertence: entre funções internas que compartilham contratos de tipos, em utilitários chamados por código já validado, e em dados que acabaram de sair do seu próprio banco.
4. Prefira adição a modificação
Estenda interfaces sem quebrar os consumidores existentes:
// Bom: adicionar campos opcionais
interface CreateTaskInput {
title: string;
description?: string;
priority?: 'low' | 'medium' | 'high'; // Adicionado depois, opcional
labels?: string[]; // Adicionado depois, opcional
}
// Ruim: mudar tipos de campos existentes ou remover campos
interface CreateTaskInput {
title: string;
// description: string; // Removido — quebra consumidores existentes
priority: number; // Mudou de string — quebra consumidores existentes
}
5. Nomenclatura previsível
| Padrão | Convenção | Exemplo |
|---|---|---|
| Endpoints REST | Substantivos no plural, sem verbos | GET /api/tasks, POST /api/tasks |
| Query params | camelCase | ?sortBy=createdAt&pageSize=20 |
| Campos de resposta | camelCase | { createdAt, updatedAt, taskId } |
| Campos booleanos | Prefixo is/has/can | isComplete, hasAttachments |
| Valores de enum | UPPER_SNAKE | "IN_PROGRESS", "COMPLETED" |
6. Padrões concretos de REST e TypeScript
Leia referencias/padroes-de-api.md somente quando chegar na implementação — contém o design de recursos REST (rotas, paginação, filtros, PATCH parcial) e os padrões de interface TypeScript (uniões discriminadas, separação input/output, branded types para IDs).
Racionalizações comuns
| Racionalização | Realidade |
|---|---|
| "Documentamos a API depois" | Os tipos SÃO a documentação. Defina-os primeiro. |
| "Não precisamos de paginação por enquanto" | Vai precisar no momento em que alguém tiver 100+ itens. Adicione desde o início. |
| "PATCH é complicado, vamos de PUT" | PUT exige o objeto completo toda vez. PATCH é o que os clientes realmente querem. |
| "Versionamos a API quando precisar" | Mudanças que quebram sem versionamento quebram consumidores. Projete para extensão desde o início. |
| "Ninguém usa aquele comportamento não documentado" | Lei de Hyrum: se é observável, alguém depende. Trate todo comportamento público como compromisso. |
| "Podemos manter duas versões" | Múltiplas versões multiplicam o custo de manutenção e criam dependências em diamante. Prefira a regra da versão única. |
| "API interna não precisa de contrato" | Consumidor interno também é consumidor. Contratos evitam acoplamento e permitem trabalho em paralelo. |
Sinais de alerta
- Endpoints que retornam formatos diferentes conforme condições
- Formatos de erro inconsistentes entre endpoints
- Validação espalhada pelo código interno em vez de concentrada nas fronteiras
- Mudanças que quebram campos existentes (troca de tipo, remoção)
- Endpoints de listagem sem paginação
- Verbos em URLs REST (
/api/createTask,/api/getUsers) - Respostas de APIs de terceiros usadas sem validação ou sanitização
Portão de aprovação
Apresente: o contrato completo (tipos de entrada e saída, formato de erros, convenções de nomenclatura) e as decisões de compatibilidade (o que é aditivo, o que exigiria versionamento) antes de implementar. O humano aprova: o formato do contrato, a estratégia de erros e qualquer mudança em interface pública existente. Só avance para a implementação após aprovação explícita.
Verificação
Após projetar uma API:
- Todo endpoint tem schemas tipados de entrada e saída
- Respostas de erro seguem um único formato consistente
- A validação acontece apenas nas fronteiras do sistema
- Endpoints de listagem suportam paginação
- Campos novos são aditivos e opcionais (retrocompatíveis)
- A nomenclatura segue convenções consistentes em todos os endpoints
- Documentação ou tipos da API são commitados junto com a implementação