Workstation Logo
Produtos
Labs de IAAgentes OpenAIAgentes ClaudeGrok BotWorkstation CRM (WSL CRM)MarketingTodos os Produtos
Soluções IA
Estações de Trabalho IAAI SME PackagesIA PrivadaClusters GPUIA EdgeLaboratório IA EmpresarialIA por Indústria
Serviços
Modernização de plataformaEngenharia digitalFundações de dados e IAOperações autónomasConsultoria de IAAutomação DevOpsCibersegurançaDesenvolvimento de softwareConstrução de agentesConfiguração MLOps
Sobre Nós
ParceirosHistórias de Clientes
Artigos
Documentação
WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7
Blog
Contacte-nosLogin
Workstation

Estações de trabalho de IA, software multiagente de IA, infraestrutura de GPU e soluções de agentes inteligentes para empresas modernas.

Contacte-nos

Soluções de IA

Estações de Trabalho IAAI SME PackagesIA PrivadaClusters GPUIA EdgeLaboratório IA EmpresarialIA por Indústria

Produtos

Todos os ProdutosWSL CRM e ERPMarketingAgentes OpenAIWSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7

Empresa

Sobre NósPor que WorkstationParceirosHistórias de ClientesPreçosContato

Recursos

ArtigosDocumentaçãoBlogPesquisarMapa do Site
Escritório Reino Unido
77-79 Marlowes, Hemel Hempstead HP1 1LFComo chegar: pegue a saída 20 da M25, Outer LondonN.º da empresa: 11641870Seg - Sex: 9:00 - 18:00 GMT
+44 7515 356 146
Escritório Bélgica
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683Seg - Sex: 9:00 - 18:00 CET
+32 492 45 67 46
Escritório Índia
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841

© 2026 Workstation AI. Todos os direitos reservados.

PrivacidadeCookiesTermos de ServiçoMapa do site

Loading blog...

Home / Blog
WebFrontendReact

Construindo sistemas backend robustos com Node.js e MySQL

Projete e implemente APIs de back-end seguros e escaláveis ​​com Node.js, Express e MySQL

Balinder Walia27 de maio de 202512 min read

Node.js e MySQL continuam sendo uma das combinações mais confiáveis ​​e testadas em batalha para a construção de sistemas back-end. O Node.js fornece um modelo de E/S sem bloqueio e orientado a eventos que lida com solicitações simultâneas de maneira eficiente, enquanto o MySQL oferece a integridade de dados relacionais que os aplicativos de negócios exigem. Juntos, eles formam uma base que alimenta tudo, desde MVPs de startups até plataformas empresariais que lidam com milhões de solicitações por dia.

Este guia aborda a construção de um back-end API de nível de produção, abordando estrutura do projeto, design de banco de dados, autenticação, tratamento de erros e implantação.

Express.js Estrutura do projeto

Node.js + Arquitetura de backend expressoCamada de middlewareAuth (JWT)ValidaçãoLimite de taxaCORSHelmetRegistroClienteNavegador/aplicativoAPIGateway/api/v1ExpressRotasGET/POST/PUT/DELControladoresTratamento de solicitaçõesServiçosLógica de negóciosMySQLDB RelacionalHTTProtachamadachamadaconsultaFormato de resposta{sucesso: verdadeiro,Dados: [...],Paginação: {...}}Pipeline de tratamento de errosApiErrorasyncHandlererrorHandlerRespostas de erro centralizadas e consistentes

Uma estrutura de projeto bem organizada é a base de um back-end sustentável. Separe as preocupações de forma clara e estabeleça convenções com antecedência.

project-root/
  src/
    config/
      database.js
      environment.js
      logger.js
    middleware/
      auth.js
      errorHandler.js
      rateLimiter.js
      validator.js
    models/
      User.js
      Product.js
      Order.js
      index.js
    routes/
      auth.routes.js
      users.routes.js
      products.routes.js
      orders.routes.js
      index.js
    services/
      auth.service.js
      user.service.js
      product.service.js
      email.service.js
    utils/
      ApiError.js
      asyncHandler.js
      pagination.js
    app.js
    server.js
  migrations/
  seeders/
  tests/
  .env
  .env.example
  package.json

O ponto de entrada configura o Express com middleware essencial:

// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const { errorHandler } = require('./middleware/errorHandler');
const routes = require('./routes');

const app = express();

// Security middleware
app.use(helmet());
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
  credentials: true,
}));

// Request parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

// Logging
app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// API routes
app.use('/api/v1', routes);

// Error handling (must be last)
app.use(errorHandler);

module.exports = app;

RESTful API Design

Projete seus endpoints API seguindo as convenções REST. Use substantivos para recursos, métodos HTTP para ações e formatos de resposta consistentes.

// src/routes/products.routes.js
const router = require('express').Router();
const { authenticate, authorize } = require('../middleware/auth');
const { validate } = require('../middleware/validator');
const { createProductSchema, updateProductSchema } = require('../validators/product');
const productController = require('../controllers/product.controller');

router.get('/', productController.getAll);
router.get('/:id', productController.getById);
router.post('/',
  authenticate,
  authorize('admin'),
  validate(createProductSchema),
  productController.create
);
router.put('/:id',
  authenticate,
  authorize('admin'),
  validate(updateProductSchema),
  productController.update
);
router.delete('/:id',
  authenticate,
  authorize('admin'),
  productController.delete
);

module.exports = router;

Os controladores devem ser finos, delegando lógica de negócios às classes de serviço:

// src/controllers/product.controller.js
const productService = require('../services/product.service');
const { asyncHandler } = require('../utils/asyncHandler');

exports.getAll = asyncHandler(async (req, res) => {
  const { page = 1, limit = 20, sort = 'created_at', order = 'DESC', search } = req.query;

  const result = await productService.findAll({
    page: parseInt(page),
    limit: Math.min(parseInt(limit), 100),
    sort,
    order,
    search,
  });

  res.json({
    success: true,
    data: result.products,
    pagination: {
      page: result.page,
      limit: result.limit,
      total: result.total,
      totalPages: result.totalPages,
    },
  });
});

exports.create = asyncHandler(async (req, res) => {
  const product = await productService.create(req.body);
  res.status(201).json({
    success: true,
    data: product,
  });
});

MySQL Pool de conexões com mysql2

MySQL Arquitetura de pool de conexãoAplicaçãoSolicitações simultâneasThread de solicitação 1Tópico de solicitação 2Tópico de solicitação 3Tópico de solicitação 4...Tópico de solicitação NConjunto de conexões (mysql2)AtivoOciosoConexão 1 - AtivoConexão 2 - AtivoConexão 3 - AtivoConexão 4 - InativoConexão 5 - InativoConexãoLimite: 10 | waitForConnections: verdadeiroenableKeepAlive: verdadeiro | queueLimit: 0MySQLServidor de banco de dadosArmazenamento de dadosÍndicesMotor InnoDBPool reutiliza conexões, evitando a sobrecarga de criação de novas por solicitação

O pool de conexões é fundamental para o desempenho. O pacotemysql2fornece um API baseado em Promise com instruções preparadas e pool de conexões prontos para uso.

// src/config/database.js
const mysql = require('mysql2/promise');
const logger = require('./logger');

const pool = mysql.createPool({
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT) || 3306,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  waitForConnections: true,
  connectionLimit: parseInt(process.env.DB_POOL_SIZE) || 10,
  queueLimit: 0,
  enableKeepAlive: true,
  keepAliveInitialDelay: 30000,
  timezone: '+00:00',
  typeCast: function (field, next) {
    if (field.type === 'TINY' && field.length === 1) {
      return field.string() === '1';
    }
    return next();
  },
});

// Test connection on startup
pool.getConnection()
  .then(conn => {
    logger.info('MySQL connected successfully');
    conn.release();
  })
  .catch(err => {
    logger.error('MySQL connection failed:', err.message);
    process.exit(1);
  });

module.exports = pool;

Sempre use consultas parametrizadas para evitar a injeção de SQL:

// NEVER do this
const query = `SELECT * FROM users WHERE email = '${email}'`;

// ALWAYS use parameterized queries
const [rows] = await pool.execute(
  'SELECT id, email, first_name, last_name FROM users WHERE email = ?',
  [email]
);

Sequelize ORM

Para aplicativos com relacionamentos de dados complexos, Sequelize fornece um ORM completo com definições de modelo, associações, migrações e criação de consultas.

// src/models/Product.js
const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize');

const Product = sequelize.define('Product', {
  id: {
    type: DataTypes.UUID,
    defaultValue: DataTypes.UUIDV4,
    primaryKey: true,
  },
  name: {
    type: DataTypes.STRING(255),
    allowNull: false,
    validate: {
      notEmpty: true,
      len: [2, 255],
    },
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
  price: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false,
    validate: {
      min: 0,
    },
  },
  sku: {
    type: DataTypes.STRING(100),
    unique: true,
    allowNull: false,
  },
  stock_quantity: {
    type: DataTypes.INTEGER,
    defaultValue: 0,
    validate: {
      min: 0,
    },
  },
  is_active: {
    type: DataTypes.BOOLEAN,
    defaultValue: true,
  },
}, {
  tableName: 'products',
  timestamps: true,
  underscored: true,
  paranoid: true, // Soft deletes
  indexes: [
    { fields: ['sku'], unique: true },
    { fields: ['is_active'] },
    { fields: ['price'] },
    { fields: ['created_at'] },
  ],
});

// Associations
Product.associate = (models) => {
  Product.belongsTo(models.Category, { foreignKey: 'category_id' });
  Product.hasMany(models.OrderItem, { foreignKey: 'product_id' });
  Product.belongsToMany(models.Tag, { through: 'product_tags' });
};

module.exports = Product;

Autenticação com JWT

Implemente autenticação sem estado usando JSON Web Tokens. Use tokens de acesso para solicitações API e tokens de atualização para gerenciamento de sessões.

// src/services/auth.service.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { User } = require('../models');
const ApiError = require('../utils/ApiError');

const SALT_ROUNDS = 12;
const ACCESS_TOKEN_EXPIRY = '15m';
const REFRESH_TOKEN_EXPIRY = '7d';

exports.register = async ({ email, password, firstName, lastName }) => {
  const existingUser = await User.findOne({ where: { email } });
  if (existingUser) {
    throw new ApiError(409, 'Email already registered');
  }

  const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
  const user = await User.create({
    email,
    password: hashedPassword,
    first_name: firstName,
    last_name: lastName,
  });

  const tokens = generateTokens(user);
  return { user: sanitizeUser(user), ...tokens };
};

exports.login = async ({ email, password }) => {
  const user = await User.findOne({ where: { email } });
  if (!user || !(await bcrypt.compare(password, user.password))) {
    throw new ApiError(401, 'Invalid email or password');
  }

  const tokens = generateTokens(user);
  return { user: sanitizeUser(user), ...tokens };
};

function generateTokens(user) {
  const accessToken = jwt.sign(
    { userId: user.id, email: user.email, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: ACCESS_TOKEN_EXPIRY }
  );

  const refreshToken = jwt.sign(
    { userId: user.id, tokenType: 'refresh' },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: REFRESH_TOKEN_EXPIRY }
  );

  return { accessToken, refreshToken };
}

function sanitizeUser(user) {
  const { password, ...userData } = user.toJSON();
  return userData;
}

O middleware de autenticação verifica tokens em rotas protegidas:

// src/middleware/auth.js
const jwt = require('jsonwebtoken');
const ApiError = require('../utils/ApiError');

exports.authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    throw new ApiError(401, 'Access token required');
  }

  const token = authHeader.split(' ')[1];
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      throw new ApiError(401, 'Access token expired');
    }
    throw new ApiError(401, 'Invalid access token');
  }
};

exports.authorize = (...roles) => {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      throw new ApiError(403, 'Insufficient permissions');
    }
    next();
  };
};

Validação de entrada com Joi

Valide todos os dados recebidos antes que eles cheguem à sua lógica de negócios. Joi fornece uma poderosa biblioteca de validação baseada em esquema.

// src/validators/product.js
const Joi = require('joi');

exports.createProductSchema = Joi.object({
  name: Joi.string().min(2).max(255).required(),
  description: Joi.string().max(5000).optional(),
  price: Joi.number().positive().precision(2).required(),
  sku: Joi.string().alphanum().max(100).required(),
  stock_quantity: Joi.number().integer().min(0).default(0),
  category_id: Joi.string().uuid().required(),
  tags: Joi.array().items(Joi.string().uuid()).optional(),
  is_active: Joi.boolean().default(true),
});

exports.updateProductSchema = Joi.object({
  name: Joi.string().min(2).max(255),
  description: Joi.string().max(5000).allow(null),
  price: Joi.number().positive().precision(2),
  stock_quantity: Joi.number().integer().min(0),
  category_id: Joi.string().uuid(),
  is_active: Joi.boolean(),
}).min(1);

// src/middleware/validator.js
exports.validate = (schema) => {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body, {
      abortEarly: false,
      stripUnknown: true,
    });

    if (error) {
      const errors = error.details.map(detail => ({
        field: detail.path.join('.'),
        message: detail.message,
      }));
      return res.status(400).json({
        success: false,
        message: 'Validation failed',
        errors,
      });
    }

    req.body = value;
    next();
  };
};

Middleware de tratamento de erros

O tratamento centralizado de erros garante respostas de erro consistentes e evita que informações confidenciais vazem para os clientes.

// src/utils/ApiError.js
class ApiError extends Error {
  constructor(statusCode, message, errors = []) {
    super(message);
    this.statusCode = statusCode;
    this.errors = errors;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

module.exports = ApiError;

// src/utils/asyncHandler.js
exports.asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// src/middleware/errorHandler.js
const logger = require('../config/logger');

exports.errorHandler = (err, req, res, next) => {
  let statusCode = err.statusCode || 500;
  let message = err.message || 'Internal Server Error';

  // Sequelize validation errors
  if (err.name === 'SequelizeValidationError') {
    statusCode = 400;
    message = 'Validation error';
  }

  // Sequelize unique constraint
  if (err.name === 'SequelizeUniqueConstraintError') {
    statusCode = 409;
    message = 'Resource already exists';
  }

  // Log server errors
  if (statusCode >= 500) {
    logger.error({
      message: err.message,
      stack: err.stack,
      url: req.originalUrl,
      method: req.method,
      ip: req.ip,
    });
  }

  res.status(statusCode).json({
    success: false,
    message,
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
    ...(err.errors?.length && { errors: err.errors }),
  });
};

Otimização e indexação de consultas

Consultas eficientes ao banco de dados são cruciais para o desempenho do back-end. Siga estas estratégias para manter suas consultas MySQL rápidas.

  • Use índices em colunas consultadas com frequência- Adicione índices em colunas usadas em cláusulas WHERE, condições JOIN e instruções ORDER BY. Os índices compostos devem seguir a regra do prefixo mais à esquerda.
  • Evite SELECT *- Sempre especifique as colunas que você precisa. Isso reduz a transferência de dados e permite que o MySQL use índices de cobertura.
  • Use EXPLAIN para analisar consultas- ExecuteEXPLAINantes de suas consultas para entender o plano de execução. Procure verificações completas de tabelas, operações de classificação de arquivos e tabelas temporárias.
  • Otimizar paginação- Para grandes conjuntos de dados, use paginação baseada em cursor (paginação de conjunto de chaves) em vez de OFFSET, que se torna lento em números de páginas altos.
// Inefficient OFFSET pagination
const [rows] = await pool.execute(
  'SELECT * FROM products ORDER BY created_at DESC LIMIT ? OFFSET ?',
  [limit, (page - 1) * limit]
);

// Efficient cursor-based pagination
const [rows] = await pool.execute(
  `SELECT id, name, price, created_at FROM products
   WHERE created_at < ?
   ORDER BY created_at DESC
   LIMIT ?`,
  [cursor, limit]
);

Migrações de banco de dados

Nunca modifique bancos de dados de produção manualmente. Use migrações Sequelize para alterações de esquema controladas por versão.

// migrations/20250101000000-create-products-table.js
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('products', {
      id: {
        type: Sequelize.UUID,
        defaultValue: Sequelize.UUIDV4,
        primaryKey: true,
      },
      name: {
        type: Sequelize.STRING(255),
        allowNull: false,
      },
      price: {
        type: Sequelize.DECIMAL(10, 2),
        allowNull: false,
      },
      sku: {
        type: Sequelize.STRING(100),
        unique: true,
        allowNull: false,
      },
      category_id: {
        type: Sequelize.UUID,
        references: {
          model: 'categories',
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      created_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
      },
      updated_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
      },
    });

    await queryInterface.addIndex('products', ['sku']);
    await queryInterface.addIndex('products', ['category_id']);
    await queryInterface.addIndex('products', ['created_at']);
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable('products');
  },
};

Limitação de taxa

Proteja sua API contra abusos com limitação de taxa. Useexpress-rate-limitcom um armazenamento Redis para implantações distribuídas.

// src/middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('../config/redis');

exports.apiLimiter = rateLimit({
  store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,
  message: {
    success: false,
    message: 'Too many requests, please try again later',
  },
  standardHeaders: true,
  legacyHeaders: false,
});

exports.authLimiter = rateLimit({
  store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
  windowMs: 15 * 60 * 1000,
  max: 5,
  message: {
    success: false,
    message: 'Too many login attempts, please try again later',
  },
  skipSuccessfulRequests: true,
});

Logging com Winston

As aplicações de produção precisam de log estruturado com vários transportes e níveis de log.

// src/config/logger.js
const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'api-server' },
  transports: [
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
      maxsize: 5242880, // 5MB
      maxFiles: 5,
    }),
    new winston.transports.File({
      filename: 'logs/combined.log',
      maxsize: 5242880,
      maxFiles: 10,
    }),
  ],
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  }));
}

module.exports = logger;

Docker Implantação

Conteinerize seu aplicativo para implantações consistentes em todos os ambientes.

# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup
COPY --from=builder /app/node_modules ./node_modules
COPY src/ ./src/
COPY migrations/ ./migrations/
COPY package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "src/server.js"]
# docker-compose.yml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=mysql
      - DB_USER=app_user
      - DB_PASSWORD_FILE=/run/secrets/db_password
      - DB_NAME=myapp
    depends_on:
      mysql:
        condition: service_healthy
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_DATABASE: myapp
      MYSQL_USER: app_user
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mysql_data:

Conclusão

Construir um back-end Node.js e MySQL robusto requer atenção à arquitetura, segurança, desempenho e questões operacionais. Ao estabelecer uma estrutura de projeto limpa, implementar autenticação e validação adequadas, otimizar consultas de banco de dados e contentorizar sua implantação, você cria um back-end seguro, de alto desempenho e fácil de manter. Comece com os fundamentos descritos neste guia, meça o desempenho do seu aplicativo sob carga realista e repita os gargalos que descobrir. Os padrões apresentados aqui foram comprovados em milhares de aplicações de produção e servirão como uma base sólida para seus sistemas back-end.