A partir da conexão com o banco de dados já estabelecida, podemos avançar para a criação de uma estrutura mais organizada para nossa aplicação utilizando o padrão MVC (Models, Views, Controllers) – embora, em APIs, geralmente não precisemos de Views. Neste tutorial, vamos criar Endpoints, Controllers e Models usando o Express para estruturar nossa aplicação de forma modular e escalável.


Estrutura Sugerida do Projeto

Uma organização comum para um projeto Node.js com Express pode ser a seguinte:

.
├── config/
│   └── db.js
├── controllers/
│   └── usuarioController.js
├── models/
│   └── usuarioModel.js
├── routes/
│   └── usuarioRoutes.js
├── .env
├── .gitignore
└── app.js

Cada pasta possui sua responsabilidade:


1. Criando o Model

No Model, encapsulamos as operações de acesso ao banco de dados. Por exemplo, crie o arquivo models/usuarioModel.js:

const db = require('../config/db');

const UsuarioModel = {
  async getAllUsuarios() {
    const result = await db.query('SELECT * FROM usuarios');
    return result.rows;
  },

  async getUsuarioById(id) {
    const result = await db.query('SELECT * FROM usuarios WHERE id = $1', [id]);
    return result.rows[0];
  },

  async createUsuario({ nome, email }) {
    const result = await db.query(
      'INSERT INTO usuarios (nome, email) VALUES ($1, $2) RETURNING *',
      [nome, email]
    );
    return result.rows[0];
  }
};

module.exports = UsuarioModel;

Aqui, estamos definindo métodos assíncronos que executam consultas usando o pool configurado em db.js.


2. Criando o Controller

O Controller atua como intermediário entre as requisições HTTP e as operações do Model. Crie o arquivo controllers/usuarioController.js:

const UsuarioModel = require('../models/usuarioModel');

const UsuarioController = {
  async listarUsuarios(req, res) {
    try {
      const usuarios = await UsuarioModel.getAllUsuarios();
      return res.status(200).json(usuarios);
    } catch (error) {
      console.error(error);
      return res.status(500).json({ error: 'Erro ao listar usuários' });
    }
  },

  async obterUsuario(req, res) {
    try {
      const { id } = req.params;
      const usuario = await UsuarioModel.getUsuarioById(id);
      if (!usuario) {
        return res.status(404).json({ error: 'Usuário não encontrado' });
      }
      return res.status(200).json(usuario);
    } catch (error) {
      console.error(error);
      return res.status(500).json({ error: 'Erro ao obter usuário' });
    }
  },

  async criarUsuario(req, res) {
    try {
      const novoUsuario = await UsuarioModel.createUsuario(req.body);
      return res.status(201).json(novoUsuario);
    } catch (error) {
      console.error(error);
      return res.status(500).json({ error: 'Erro ao criar usuário' });
    }
  }
};

module.exports = UsuarioController;

Cada método do controller trata da lógica necessária para processar a requisição, interage com o model e envia a resposta adequada.