conventions.md 4.1 KB

Convenções — Backend

Como criar um novo domínio

Siga a sequência abaixo ao adicionar um novo módulo ao backend. O exemplo usa o domínio hipotético Produto.


1. Schema de validação (se houver body a validar)

Crie src/middleware/schemas/Produto.Schema.js:

import { z } from "zod";

export const criarProdutoSchema = z.object({
  nome: z.string().min(2).max(100),
  preco: z.number().positive(),
});

2. Model Objection (se houver tabela no banco)

Crie src/models/Produto.model.js:

import { Model } from "objection";

export class Produto extends Model {
  static get tableName() { return "produtos"; }
}

Crie também a migration correspondente em db/migrations/ (arquivos .cjs; o diretório é configurado no knexfile.js).


3. Service

Crie src/services/produtoService.js com toda a lógica de negócio:

import { Produto } from "#models/Produto.model.js";

export async function listarProdutos() {
  return Produto.query().orderBy("Nome");
}

export async function criarProduto(dados) {
  return Produto.query().insert(dados);
}

4. Controller

Crie src/controllers/Produto.Controller.js. Controllers são finos — apenas extraem dados da requisição e delegam ao service:

import * as produtoService from "#services/produtoService.js";
import { AppError } from "#shared/errors/AppError.js";

export const ProdutoController = {
  Listar: async function (req, res, next) {
    try {
      const produtos = await produtoService.listarProdutos();
      res.json({ produtos });
    } catch (err) {
      next(err);
    }
  },

  Criar: async function (req, res, next) {
    try {
      const produto = await produtoService.criarProduto(req.body);
      res.status(201).json({ produto });
    } catch (err) {
      next(err);
    }
  },
};

5. Rotas

Crie src/routes/Produto.Rotas.js:

import { Router } from "express";
import { ProdutoController } from "#controllers/Produto.Controller.js";
import { requireUser } from "#middleware/RequireUser.js";
import { requireAdmin } from "#middleware/RequireAdmin.js";
import { validate } from "#middleware/Validate.js";
import { criarProdutoSchema } from "#middleware/schemas/Produto.Schema.js";

export const produtosRouter = Router();

produtosRouter.get("/",  requireUser,                                    ProdutoController.Listar);
produtosRouter.post("/", requireUser, requireAdmin, validate({ body: criarProdutoSchema }), ProdutoController.Criar);

6. Registro no Roteamento

Adicione o novo router em src/routes/index.js:

import { produtosRouter } from "./Produto.Rotas.js";

// dentro de IniciarRoteamento(app):
app.use("/api/produtos", produtosRouter);

Regras gerais

Nomenclatura:

  • Arquivos de rotas: NomeDoModulo.Rotas.js (PascalCase)
  • Arquivos de controller: NomeDoModulo.Controller.js (PascalCase)
  • Arquivos de service: nomeDoModuloService.js (camelCase)
  • Arquivos de model: NomeDoModulo.model.js (PascalCase)
  • Arquivos de schema: NomeDoModulo.Schema.js (PascalCase)

Imports: o código atual usa caminhos relativos em todo o src/ — siga esse padrão para manter consistência:

import { db } from "../config/db.config.js";

O package.json define o alias #*./src/*, que é válido e funciona, mas ainda não é usado em nenhum arquivo do src/. Por ora, prefira imports relativos para alinhar ao restante do código. (Os exemplos acima usam # apenas para ilustrar; o código real usa ../.)

Auth nas rotas:

  • Leitura pública: sem middleware de auth
  • Leitura autenticada: requireUser
  • Escrita de dados globais / operações destrutivas: requireUser, requireAdmin
  • Consulte auth.md para entender os níveis de acesso

Tratamento de erros:

  • Sempre encapsule o corpo do controller em try/catch e passe o erro para next(err)
  • Use AppError para erros esperados (ex: não encontrado, sem permissão)
  • O ErrorHandler global formata a resposta automaticamente

Documentação:

  • Ao criar novas rotas, adicione os endpoints em api.md