Jumentix
Blueprint de API REST

APIs REST orientadas a contratos sem aprisionar o domínio

Use OpenAPI 3.1 para conectar validação, handlers, controllers, SDKs e documentação.

Mascote open source do Jumentix

O que este blueprint entrega à sua equipe

  • Adaptadores nativos para vários runtimes HTTP Node.js
  • Documentação Swagger e arquivos estáticos
  • Duas camadas de validação: interface e domínio
export class TaskController {
  constructor(private readonly createTask: CreateTaskUseCase) {}

  async create(input: CreateTaskInput): Promise<TaskOutput> {
    return this.createTask.execute(input);
  }
}

Do zero ao primeiro MVP

Uma sequência prática de lançamento para este blueprint

Use este caminho quando o primeiro MVP precisa expor CRUD previsível ou comportamento de integração que outro sistema, tela admin ou frontend consiga chamar imediatamente.

Escolha o primeiro recurso

Modele um recurso como Task com Category, campos obrigatórios, validação e as primeiras operações de create/list.

  • Escreva os tipos Category e Task com campos obrigatórios e a flag completed.
  • Semeie a categoria work para que o primeiro comando tenha um dono válido.
  • Escolha create e list como únicas operações do MVP.
  • Escreva critérios de aceite: título vazio retorna 400, categoria desconhecida retorna 404.

Saída MVP: uma superfície OpenAPI pequena com um comando e uma query.

type Category = { id: string; name: string };
type Task = { id: string; title: string; categoryId: string; completed: boolean };

const categories = new Map<string, Category>([
  ['work', { id: 'work', name: 'Work' }]
]);
const tasks = new Map<string, Task>();

Critérios de pronto do primeiro MVP

  • O contrato OpenAPI e o handler em execução descrevem as mesmas rotas e schemas.
  • Um client tipado cria e lista tasks sem ler o código fonte do servidor.
  • Input inválido falha com 400 e categoria desconhecida com 404 antes da persistência.
  • Testes unitários, checks de rota e um request de fumaça capturado provam a fatia antes da demo.

Fora de escopo de propósito

  • Operações bulk e endpoints de relatório.
  • Webhooks e entrega de eventos outbound.
  • Rate limiting, autenticação e política de versionamento de API.
  • Adaptador de banco de produção — a troca acontece após o primeiro feedback.

Escopo MVP

Mantenha a primeira versão pequena o bastante para comprovar

Uma família de recurso

Task e Category bastam para provar CRUD, filtro, validação e ownership.

Fatia de domínio

Um grupo de rotas

Create, list, update status e fetch by id antes de reporting ou operações bulk.

Fatia de API

Um perfil de adapter

Comece com in-memory ou SQL local, depois troque o repository adapter após feedback.

Fatia de runtime
Área de provaPergunta a responderMecanismo JumentixEvidência MVP
ContratoOutro client entende a API sem ler o código fonte?OpenAPI 3.1, check de rota, exemplos de schema.Docs e metadados de rota batem com o handler.
ComportamentoO primeiro fluxo aplica validação e regras de domínio?Testes de controller/use-case e checks de contrato de erro.Input inválido falha antes da persistência; erros de domínio ficam explícitos.
AdoçãoUm frontend ou parceiro consegue chamar hoje?Client REST gerado e exemplo copiável de request.Um primeiro consumidor consegue criar e listar registros.

Implementação prática

Código completo para a primeira fatia funcional

Estes exemplos mantêm Category e Task como vocabulário de produto e mostram controller, contrato, client, worker ou camada de estado necessários para chegar a um MVP executável.

type Category = { id: string; name: string };
type Task = { id: string; title: string; categoryId: string; completed: boolean };

const categories = new Map<string, Category>([
  ['work', { id: 'work', name: 'Work' }]
]);
const tasks = new Map<string, Task>();

class CreateTaskUseCase {
  async execute(input: { title: string; categoryId: string }) {
    if (!input.title.trim()) return { status: 400, body: { error: 'title is required' } };
    if (!categories.has(input.categoryId)) return { status: 404, body: { error: 'category not found' } };

    const task: Task = {
      id: crypto.randomUUID(),
      title: input.title,
      categoryId: input.categoryId,
      completed: false
    };
    tasks.set(task.id, task);
    return { status: 201, body: task };
  }
}

class TaskController {
  constructor(private readonly createTask: CreateTaskUseCase) {}

  async create(request: Request) {
    const input = await request.json() as { title: string; categoryId: string };
    const response = await this.createTask.execute(input);
    return Response.json(response.body, { status: response.status });
  }

  async list() {
    return Response.json([...tasks.values()]);
  }
}

const controller = new TaskController(new CreateTaskUseCase());

export async function handleRequest(request: Request) {
  const url = new URL(request.url);
  if (request.method === 'POST' && url.pathname === '/tasks') return controller.create(request);
  if (request.method === 'GET' && url.pathname === '/tasks') return controller.list();
  return Response.json({ error: 'not found' }, { status: 404 });
}

Construa o produto. Preserve a arquitetura.

Explore o código, execute a fábrica localmente e transforme seu próximo serviço Node.js em uma capacidade repetível de plataforma.