VierCa Logo
Home/Blog/Node.js para Iniciantes: Construindo APIs Robustas
Voltar ao Blog
Desenvolvimento de Sites Codificado

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.

Equipe VierCa
5 Dez, 2024
12 min de leitura

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ê!