kb.erickguedes.com
Node.js: Backend JavaScript em Produção

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 Router para modularizar, middleware de erro com 4 parâmetros para tratamento centralizado, e pacotes como helmet e cors para segurança básica.