Node.js para Iniciantes: Construindo APIs Robustas
Guia prático para criar APIs escaláveis com Node.js, Express e melhores práticas de desenvolvimento.
Node.js revolucionou o desenvolvimento backend ao permitir que desenvolvedores JavaScript criem APIs robustas e escaláveis. Neste guia, você aprenderá desde os conceitos básicos até técnicas avançadas para construir APIs profissionais.
Por que Node.js?
Node.js oferece várias vantagens para desenvolvimento de APIs:
- JavaScript em todo lugar: Mesma linguagem no frontend e backend
- Performance: Event loop não-bloqueante
- NPM: Maior ecossistema de pacotes do mundo
- Escalabilidade: Ideal para aplicações I/O intensivas
- Comunidade: Suporte ativo e abundante documentação
Configurando o Ambiente
Instalação do Node.js
Baixe a versão LTS do site oficial e instale em seu sistema.
Inicializando o Projeto
mkdir minha-api
cd minha-api
npm init -y
Instalando Dependências
npm install express cors helmet morgan dotenv
npm install -D nodemon
Estrutura Básica da API
Arquivo Principal (app.js)
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middlewares
app.use(helmet()); // Segurança
app.use(cors()); // CORS
app.use(morgan('combined')); // Logs
app.use(express.json()); // Parse JSON
app.use(express.urlencoded({ extended: true }));
// Rota básica
app.get('/', (req, res) => {
res.json({
message: 'API funcionando!',
version: '1.0.0'
});
});
// Iniciar servidor
app.listen(PORT, () => {
console.log(`Servidor rodando na porta ${PORT}`);
});
Criando Rotas RESTful
Estrutura de Pastas
projeto/
├── controllers/
├── routes/
├── models/
├── middleware/
├── config/
└── app.js
Exemplo de Rota de Usuários
// routes/users.js
const express = require('express');
const router = express.Router();
// GET /users - Listar usuários
router.get('/', (req, res) => {
res.json({ users: [] });
});
// GET /users/:id - Buscar usuário
router.get('/:id', (req, res) => {
const { id } = req.params;
res.json({ user: { id, name: 'João' } });
});
// POST /users - Criar usuário
router.post('/', (req, res) => {
const { name, email } = req.body;
res.status(201).json({
message: 'Usuário criado',
user: { id: 1, name, email }
});
});
// PUT /users/:id - Atualizar usuário
router.put('/:id', (req, res) => {
const { id } = req.params;
const { name, email } = req.body;
res.json({
message: 'Usuário atualizado',
user: { id, name, email }
});
});
// DELETE /users/:id - Deletar usuário
router.delete('/:id', (req, res) => {
const { id } = req.params;
res.json({ message: `Usuário ${id} deletado` });
});
module.exports = router;
Middlewares Essenciais
Middleware de Validação
// middleware/validation.js
const validateUser = (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
error: 'Nome e email são obrigatórios'
});
}
if (!email.includes('@')) {
return res.status(400).json({
error: 'Email inválido'
});
}
next();
};
module.exports = { validateUser };
Middleware de Autenticação
// middleware/auth.js
const jwt = require('jsonwebtoken');
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token não fornecido' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Token inválido' });
}
req.user = user;
next();
});
};
module.exports = { authenticateToken };
Tratamento de Erros
Middleware Global de Erro
// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
// Erro de validação
if (err.name === 'ValidationError') {
return res.status(400).json({
error: 'Dados inválidos',
details: err.message
});
}
// Erro de duplicação
if (err.code === 11000) {
return res.status(409).json({
error: 'Recurso já existe'
});
}
// Erro padrão
res.status(500).json({
error: 'Erro interno do servidor'
});
};
module.exports = errorHandler;
Conectando com Banco de Dados
MongoDB com Mongoose
// config/database.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('MongoDB conectado');
} catch (error) {
console.error('Erro ao conectar MongoDB:', error);
process.exit(1);
}
};
module.exports = connectDB;
Modelo de Usuário
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true,
minlength: 6
}
}, {
timestamps: true
});
module.exports = mongoose.model('User', userSchema);
Segurança da API
Rate Limiting
// middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100, // máximo 100 requests por IP
message: {
error: 'Muitas tentativas, tente novamente em 15 minutos'
}
});
module.exports = limiter;
Sanitização de Dados
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
// Usar nos middlewares
app.use(mongoSanitize()); // Previne NoSQL injection
app.use(xss()); // Previne XSS attacks
Testes da API
Configurando Jest e Supertest
npm install -D jest supertest
Exemplo de Teste
// tests/users.test.js
const request = require('supertest');
const app = require('../app');
describe('Users API', () => {
test('GET /users deve retornar lista de usuários', async () => {
const response = await request(app)
.get('/users')
.expect(200);
expect(response.body).toHaveProperty('users');
expect(Array.isArray(response.body.users)).toBe(true);
});
test('POST /users deve criar novo usuário', async () => {
const userData = {
name: 'João Silva',
email: 'joao@email.com'
};
const response = await request(app)
.post('/users')
.send(userData)
.expect(201);
expect(response.body.user.name).toBe(userData.name);
expect(response.body.user.email).toBe(userData.email);
});
});
Documentação da API
Swagger/OpenAPI
npm install swagger-jsdoc swagger-ui-express
// config/swagger.js
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Minha API',
version: '1.0.0',
description: 'API desenvolvida com Node.js e Express'
},
servers: [
{
url: 'http://localhost:3000',
description: 'Servidor de desenvolvimento'
}
]
},
apis: ['./routes/*.js']
};
const specs = swaggerJsdoc(options);
module.exports = { swaggerUi, specs };
Deploy e Produção
Variáveis de Ambiente
# .env
NODE_ENV=production
PORT=3000
MONGODB_URI=mongodb://localhost:27017/minha-api
JWT_SECRET=seu-jwt-secret-super-seguro
Scripts do Package.json
{
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "jest",
"test:watch": "jest --watch"
}
}
Monitoramento e Logs
Winston para Logs
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
Melhores Práticas
- Versionamento: Use /api/v1/ nas rotas
- Status Codes: Use códigos HTTP apropriados
- Paginação: Implemente para listas grandes
- Cache: Use Redis para dados frequentes
- Validação: Valide todos os inputs
- Logs: Registre operações importantes
- Testes: Mantenha cobertura alta
Conclusão
Construir APIs robustas com Node.js requer atenção a detalhes como segurança, performance, testes e documentação. Seguindo as práticas apresentadas neste guia, você estará preparado para criar APIs profissionais e escaláveis.
Na VierCa, utilizamos essas técnicas em todos os nossos projetos backend, garantindo APIs confiáveis e performáticas para nossos clientes.
Precisa de uma API customizada para seu projeto? Entre em contato e vamos desenvolver a solução perfeita para você!