Melhores Práticas para Estruturar Projetos Express.js
Express.js é um framework minimalista e flexível para Node.js, amplamente utilizado para criar aplicações web e APIs. Estruturar adequadamente um projeto Express.js é essencial para facilitar a manutenção, melhorar a escalabilidade e garantir a eficiência do desenvolvimento. Este artigo apresenta as melhores práticas para estruturar projetos Express.js, abordando desde a organização de arquivos e pastas até o uso de middlewares e boas práticas de codificação.
Pré-requisitos
Para seguir este guia, você precisará de:
- Conhecimento básico de JavaScript e Node.js
- Experiência com Express.js
- Node.js e npm instalados em seu ambiente de desenvolvimento
Setup Steps
Passo 1: Configuração do Ambiente
Primeiro, instale o Node.js e o npm (Node Package Manager) no seu sistema. Acesse o site oficial do Node.js para baixar e instalar a versão recomendada.
Passo 2: Inicializar o Projeto
Crie uma nova pasta para o seu projeto e inicialize um novo projeto Node.js com o npm:
mkdir meu-projeto-express
cd meu-projeto-express
npm init -y
Passo 3: Instalar Dependências
Instale as dependências principais, incluindo o Express.js:
npm install express body-parser mongoose dotenv
Instale também as dependências de desenvolvimento para facilitar o trabalho durante o desenvolvimento:
npm install --save-dev nodemon
Estrutura de Pastas
A organização das pastas é crucial para manter o código limpo e modular. Abaixo está um exemplo de estrutura de pastas recomendada para um projeto Express.js:
project-root/
│
├── src/
│ ├── config/
│ │ └── db.js
│ ├── controllers/
│ │ └── userController.js
│ ├── models/
│ │ └── userModel.js
│ ├── routes/
│ │ └── userRoutes.js
│ ├── middlewares/
│ │ └── authMiddleware.js
│ ├── utils/
│ │ └── helper.js
│ ├── app.js
│ └── server.js
│
├── test/
│ └── userController.test.js
│
├── node_modules/
│
├── .env
├── .gitignore
├── package.json
├── package-lock.json
└── README.md
Organização do Código
app.js
O arquivo app.js
é onde você configura e inicia o Express.js, configura os middlewares e define as rotas principais.
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const userRoutes = require('./routes/userRoutes');
// Middlewares
app.use(bodyParser.json());
// Rotas
app.use('/api/users', userRoutes);
module.exports = app;
server.js
O arquivo server.js
é responsável por iniciar o servidor.
const app = require('./app');
const dotenv = require('dotenv');
dotenv.config();
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Configuração do Banco de Dados
Coloque todas as configurações do banco de dados em uma pasta separada (config/
). Isso facilita a modificação e a reutilização do código.
// config/db.js
const mongoose = require('mongoose');
const dotenv = require('dotenv');
dotenv.config();
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
});
console.log('MongoDB connected');
} catch (error) {
console.error('MongoDB connection error:', error);
process.exit(1);
}
};
module.exports = connectDB;
Controladores
Os controladores devem conter a lógica das rotas e interagir com os modelos. Mantenha-os simples e focados em suas responsabilidades específicas.
// controllers/userController.js
const User = require('../models/userModel');
const getUsers = async (req, res) => {
try {
const users = await User.find();
res.status(200).json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
const createUser = async (req, res) => {
const user = new User(req.body);
try {
await user.save();
res.status(201).json(user);
} catch (error) {
res.status(400).json({ message: error.message });
}
};
module.exports = { getUsers, createUser };
Modelos
Os modelos devem representar a estrutura dos dados e interagir com o banco de dados.
// models/userModel.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
}, {
timestamps: true
});
const User = mongoose.model('User', userSchema);
module.exports = User;
Rotas
As rotas definem os endpoints e chamam os controladores apropriados.
// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const { getUsers, createUser } = require('../controllers/userController');
router.get('/', getUsers);
router.post('/', createUser);
module.exports = router;
Middlewares
Os middlewares são utilizados para interceptar e manipular requisições antes que elas alcancem os controladores.
// middlewares/authMiddleware.js
const jwt = require('jsonwebtoken');
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization');
if (!token) {
return res.status(401).json({ message: 'No token, authorization denied' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded.user;
next();
} catch (error) {
res.status(401).json({ message: 'Token is not valid' });
}
};
module.exports = authMiddleware;
Testes
Mantenha os testes em uma pasta separada (test/
) e use frameworks de teste como Mocha, Chai ou Jest para garantir a qualidade do código.
// test/userController.test.js
const request = require('supertest');
const app = require('../src/app');
describe('GET /api/users', () => {
it('should return all users', async () => {
const res = await request(app).get('/api/users');
expect(res.statusCode).toBe(200);
expect(res.body).toBeInstanceOf(Array);
});
});
Conclusão
Seguir estas melhores práticas para estruturar projetos Express.js ajudará a manter o código organizado, modular e fácil de manter. Estruturar o código adequadamente é fundamental para o sucesso a longo prazo de qualquer projeto de desenvolvimento. Adote essas práticas desde o início do seu projeto para garantir uma base sólida e escalável.