Express: Construindo APIs REST
Aula 2 de 7
Introdução ao Express
Express é o framework web mais popular para Node.js. Minimalista, flexível e com enorme ecossistema de middleware.
npm install express
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Servidor rodando em http://localhost:${port}`);
});
Rotas e Router
// server.js
const express = require('express');
const app = express();
const userRoutes = require('./routes/users');
app.use('/users', userRoutes);
// routes/users.js
const { Router } = require('express');
const router = Router();
// GET /users
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});
// GET /users/:id
router.get('/:id', (req, res) => {
const { id } = req.params;
res.json({ id: Number(id), name: `User ${id}` });
});
// POST /users
router.post('/', (req, res) => {
const { name } = req.body;
res.status(201).json({ id: 3, name });
});
// PUT /users/:id
router.put('/:id', (req, res) => {
res.json({ id: Number(req.params.id), ...req.body });
});
// DELETE /users/:id
router.delete('/:id', (req, res) => {
res.status(204).send();
});
module.exports = router;
Parâmetros de Rota e Query
// /users?page=2&limit=10
router.get('/', (req, res) => {
const { page = 1, limit = 10 } = req.query;
res.json({ page: Number(page), limit: Number(limit) });
});
// /users/:userId/posts/:postId
router.get('/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({ userId: Number(userId), postId: Number(postId) });
});
Middleware
Middleware são funções que têm acesso ao req, res e next. Podem modificar requisições, encerrar respostas ou chamar o próximo middleware.
// Middleware global
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Middleware de log
app.use((req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
});
// Middleware de autenticação (simples)
const authMiddleware = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).json({ error: 'Token não fornecido' });
}
req.user = { id: 1, role: 'admin' };
next();
};
// Middleware por rota
router.get('/profile', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
Tratamento de Erros
// Middleware de erro (4 parâmetros)
app.use((err, req, res, next) => {
console.error('Erro:', err.stack);
const status = err.status || 500;
const message = err.status ? err.message : 'Erro interno do servidor';
res.status(status).json({
error: message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
});
// Criando erros com status
class AppError extends Error {
constructor(message, status) {
super(message);
this.status = status;
}
}
router.get('/:id', async (req, res, next) => {
try {
const user = await findUser(req.params.id);
if (!user) throw new AppError('Usuário não encontrado', 404);
res.json(user);
} catch (err) {
next(err);
}
});
Body Parsing e Validação
const express = require('express');
const app = express();
app.use(express.json()); // application/json
app.use(express.urlencoded({ extended: true })); // application/x-www-form-urlencoded
app.use(express.text()); // text/plain
app.use(express.raw()); // application/octet-stream
Segurança com CORS e Helmet
npm install cors helmet
const cors = require('cors');
const helmet = require('helmet');
app.use(helmet());
app.use(cors({
origin: 'https://meuapp.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// CORS para múltiplas origens
const allowedOrigins = ['https://app1.com', 'https://app2.com'];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Origem não permitida'));
}
}
}));
Arquivos Estáticos
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
// http://localhost:3000/style.css -> ./public/style.css
// Com prefixo virtual
app.use('/static', express.static(path.join(__dirname, 'public')));
// http://localhost:3000/static/style.css -> ./public/style.css
Lab: Exercício - API REST Completa
Crie uma API de tarefas (todo list):
// index.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const todoRoutes = require('./routes/todos');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(helmet());
app.use(cors());
app.use(express.json());
app.use('/todos', todoRoutes);
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status || 500).json({
error: err.message || 'Erro interno'
});
});
app.listen(PORT, () => {
console.log(`API rodando na porta ${PORT}`);
});
// routes/todos.js
const { Router } = require('express');
const router = Router();
let todos = [
{ id: 1, title: 'Estudar Node.js', completed: false },
{ id: 2, title: 'Criar API REST', completed: true }
];
let nextId = 3;
router.get('/', (req, res) => {
const { completed } = req.query;
let result = todos;
if (completed !== undefined) {
result = todos.filter(t => t.completed === (completed === 'true'));
}
res.json(result);
});
router.get('/:id', (req, res) => {
const todo = todos.find(t => t.id === Number(req.params.id));
if (!todo) {
return res.status(404).json({ error: 'Tarefa não encontrada' });
}
res.json(todo);
});
router.post('/', (req, res) => {
const { title } = req.body;
if (!title) {
return res.status(400).json({ error: 'Título é obrigatório' });
}
const todo = { id: nextId++, title, completed: false };
todos.push(todo);
res.status(201).json(todo);
});
router.put('/:id', (req, res) => {
const todo = todos.find(t => t.id === Number(req.params.id));
if (!todo) {
return res.status(404).json({ error: 'Tarefa não encontrada' });
}
const { title, completed } = req.body;
if (title !== undefined) todo.title = title;
if (completed !== undefined) todo.completed = completed;
res.json(todo);
});
router.delete('/:id', (req, res) => {
const index = todos.findIndex(t => t.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'Tarefa não encontrada' });
}
todos.splice(index, 1);
res.status(204).send();
});
module.exports = router;
# Testar a API
curl -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"title": "Nova tarefa"}'
curl http://localhost:3000/todos
curl -X PUT http://localhost:3000/todos/1 \
-H "Content-Type: application/json" \
-d '{"completed": true}'
curl -X DELETE http://localhost:3000/todos/1
Express organiza sua API em torno de middleware e rotas. Use
Routerpara modularizar, middleware de erro com 4 parâmetros para tratamento centralizado, e pacotes comohelmetecorspara segurança básica.