API — Visão Geral
Esta página descreve os contratos, convenções e comportamentos comuns de toda a camada de API do sistema LJ Velas & Aromas.
1. Base URLs
| Ambiente | URL | Observação |
|---|---|---|
| Local | http://localhost:3000 | Next.js dev server (npm run dev) |
| Staging | https://beige-cassowary-235127.hostingersite.com | Subdomínio Hostinger — deploy automático via CI |
| Produção | https://ljvelasearomas.com.br | Domínio próprio — deploy manual controlado |
Todas as rotas de API ficam sob o prefixo /api/ em qualquer ambiente.
2. Segmentos de API
| Prefixo | Autenticação | Propósito | CORS |
|---|---|---|---|
/api/auth/* | Pública (rate limited) | Login, logout, refresh, registro | Same-origin |
/api/products/* | Pública | Listagem e detalhe de produtos | Aberto (*) |
/api/categories | Pública | Listagem de categorias | Aberto (*) |
/api/shipping/* | Pública | Cálculo de frete (Correios / Melhor Envio) | Aberto (*) |
/api/coupons/* | Pública | Validação de cupons de desconto | Aberto (*) |
/api/orders/* | JWT cookie — CUSTOMER | Criação e consulta de pedidos do usuário | Same-origin |
/api/user/* | JWT cookie — CUSTOMER | Perfil, endereços e histórico do cliente | Same-origin |
/api/payments/* | JWT cookie — CUSTOMER | Initiação e status de pagamentos (Pagar.me) | Same-origin |
/api/admin/* | JWT cookie — ADMIN | Painel administrativo (CRUD completo) | Same-origin |
/api/v1/* | Bearer API Key | API pública versionada para integrações externas | Configurável |
/api/webhooks/stock | Bearer API Key (stock:write) | Atualização de estoque via integração ERP | Same-origin |
Same-origin significa que apenas requisições vindas do próprio domínio são aceitas (sem header
Access-Control-Allow-Originaberto).
3. Formato de Resposta
Todas as respostas utilizam Content-Type: application/json.
Sucesso — o corpo retorna diretamente os dados da operação:
// GET /api/products/42
{
"id": 42,
"name": "Vela Aromática Lavanda",
"price": 3990,
"stock": 14
}
Erro — estrutura padronizada com campo obrigatório error e campo opcional details:
// 422 Unprocessable Entity
{
"error": "Dados inválidos",
"details": {
"name": "Campo obrigatório",
"price": "Deve ser maior que zero"
}
}
Preços são sempre representados em centavos (inteiro), ex.:
R$ 39,90→3990.
4. Paginação
Rotas que retornam listas aceitam os query params:
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | inteiro | 1 | Número da página (base 1) |
limit | inteiro | 20 | Itens por página (máximo: 100) |
A resposta de listas inclui um objeto pagination no nível raiz:
{
"data": [ /* itens */ ],
"pagination": {
"total": 87,
"page": 2,
"limit": 20,
"totalPages": 5
}
}
5. Códigos de Status HTTP
| Status | Nome | Quando ocorre |
|---|---|---|
200 | OK | Requisição bem-sucedida (GET, PATCH, DELETE) |
201 | Created | Recurso criado com sucesso (POST) |
400 | Bad Request | Corpo ou query params malformados / tipo incorreto |
401 | Unauthorized | Token ausente, expirado ou inválido |
403 | Forbidden | Token válido, mas papel insuficiente (ex.: CUSTOMER acessando /api/admin) |
404 | Not Found | Recurso não encontrado pelo ID informado |
409 | Conflict | Violação de unicidade (ex.: e-mail já cadastrado, SKU duplicado) |
422 | Unprocessable Entity | Dados semanticamente inválidos (validação Zod falhou) |
429 | Too Many Requests | Limite de taxa atingido (ver seção Rate Limiting) |
500 | Internal Server Error | Erro não tratado no servidor — verifique os logs |
6. Rate Limiting
O rate limiting está ativo somente na rota /api/auth/login para mitigar ataques de força-bruta.
| Configuração | Valor |
|---|---|
| Janela | 15 minutos |
| Limite | 10 tentativas por IP |
| Armazenamento | In-memory (reinicia com o processo) |
Quando o limite é atingido, a resposta 429 inclui o header:
Retry-After: 847 // segundos restantes até o reset da janela
7. Uploads de Arquivos
Imagens de produtos e banners são enviadas via POST /api/admin/upload.
| Campo | Valor |
|---|---|
| Content-Type | multipart/form-data |
| Campo do arquivo | file |
| Formatos aceitos | image/jpeg, image/png, image/webp, image/gif |
| Tamanho máximo | 5 MB por arquivo |
| Resposta de sucesso | { "url": "https://..." } |
Exemplo de requisição com curl:
curl -X POST https://ljvelasearomas.com.br/api/admin/upload \
-H "Cookie: token=<jwt>" \
-F "file=@foto-produto.webp"
As imagens são armazenadas no diretório público do servidor Hostinger e servidas diretamente via URL.
8. Webhooks
8.1 Outbound — Notificações do Sistema
O sistema pode disparar webhooks para URLs externas configuradas no painel admin ao ocorrerem eventos como order.created, order.paid e order.shipped.
Assinatura (HMAC-SHA256):
Cada requisição carrega o header X-Webhook-Signature com a assinatura HMAC-SHA256 do corpo da requisição, usando a chave secreta configurada:
X-Webhook-Signature: sha256=a3f9c...
Para validar no receptor:
const expectedSig = "sha256=" + createHmac("sha256", WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
if (sig !== expectedSig) return res.status(401).end();
8.2 Inbound — Atualização de Estoque
| Propriedade | Valor |
|---|---|
| Rota | POST /api/webhooks/stock |
| Autenticação | Authorization: Bearer <api-key> com escopo stock:write |
| Corpo esperado | { "sku": string, "quantity": number } |
| Resposta | { "updated": true } ou erro padronizado |
9. Versionamento
A API possui duas camadas distintas:
| Camada | Prefixo | Propósito |
|---|---|---|
| Interna | /api/ | Rotas Next.js App Router (app/api/). Consumidas exclusivamente pelo frontend da loja. Podem mudar entre deploys sem aviso. |
| Pública v1 | /api/v1/ | API versionada e estável para integrações externas (ERPs, marketplaces, automações). Breaking changes só em nova versão (/api/v2/). |
Regra prática: se você está construindo uma integração externa, use sempre
/api/v1/. As rotas/api/sem versão são contratos internos e não possuem garantia de estabilidade.