HomeBanco de dadosCriar um App em Tempo Real Node.js com MySQL

Criar um App em Tempo Real Node.js com MySQL

Este guia completo detalha a integração de um banco de dados MySQL em uma aplicação em tempo real com Node.js e Socket.io. Você aprenderá etapas práticas para criar um sistema de votação persistente onde os dados sobrevivem a reinicializações do servidor, projetado para desenvolvedores que buscam aprimorar seus apps em tempo real com armazenamento de dados confiável.

Em tutoriais anteriores, aplicações Node.js em tempo real armazenavam dados apenas na memória volátil, perdendo-os ao reiniciar o servidor. Para produção, armazenamento persistente é essencial. O banco de dados MySQL foi escolhido por sua confiabilidade e ampla adoção. Portanto, este guia foca na integração do MySQL para construir uma aplicação Node.js em tempo real robusta.

Este projeto é uma extensão de um tutorial básico de aplicação de votação em tempo real. Certifique-se de ter configurado seu ambiente de desenvolvimento Node.js e o projeto base antes de prosseguir.

Passos para Integrar MySQL em um App Node.js em Tempo Real

A integração do banco de dados envolve várias etapas sequenciais importantes. Primeiro, certifique-se de que o MySQL está instalado em seu sistema baixando o instalador oficial no site do MySQL. Em seguida, crie um banco de dados e uma tabela dedicada para este projeto.

1. Criar o Esquema da Tabela de Votação

O esquema da tabela define a estrutura de armazenamento de dados. Execute o comando SQL a seguir em um cliente MySQL como o MySQL Workbench ou phpMyAdmin. Esta tabela registrará cada voto com um carimbo de data e hora automático.

CREATE TABLE `voting` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(12) NOT NULL,
  `time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Explicação do Esquema: A coluna id é uma chave primária de auto incremento. A coluna name armazena o nome do candidato (por exemplo, “akbar” ou “goldie”). A coluna time registra automaticamente o carimbo de data e hora do voto. O mecanismo InnoDB é preferido por seu suporte a transações e chaves estrangeiras.

2. Instalar o Driver MySQL para Node.js

O Node.js requer um driver específico para se comunicar com o MySQL. O pacote mysql2 é recomendado em vez de mysql porque suporta Promises nativamente, resultando em um código mais limpo e gerenciável. Abra um terminal no diretório do seu projeto e execute o comando a seguir.

npm install mysql2

Esta instalação adiciona o driver ao arquivo package.json do seu projeto. Certifique-se de estar no mesmo diretório do seu arquivo server.js ao executar este comando.

3. Configurar a Conexão do Banco de Dados em server.js

O próximo passo é editar o arquivo server.js. Crie um pool de conexões para o banco de dados MySQL usando o driver instalado. Uma configuração de pool é mais eficiente para lidar com conexões simultâneas do que uma única conexão.

const mysql = require('mysql2/promise');

// Criar um pool de conexões MySQL
const pool = mysql.createPool({
    host: 'localhost',
    user: 'root', // Substitua pelo seu usuário MySQL
    password: '', // Substitua pela sua senha MySQL
    database: 'seu_nome_de_banco_de_dados', // Substitua pelo nome do seu BD
    waitForConnections: true,
    connectionLimit: 10,
    queueLimit: 0
});

Detalhes da Configuração: A opção connectionLimit: 10 define o número máximo de conexões no pool. waitForConnections: true permite que as requisições aguardem se o pool estiver cheio. queueLimit: 0 significa que não há limite de fila. Sempre use variáveis de ambiente para armazenar credenciais do banco de dados em produção.

4. Criar Funções para Recuperar e Armazenar Dados

A aplicação requer duas funções principais. A primeira inicializa os dados quando o servidor inicia. A segunda lida com o salvamento de novos dados sempre que um usuário vota. O uso de async/await resulta em um código mais legível comparado aos callbacks.

// Variável global para dados em memória
let voteCount = { akbar: 0, goldie: 0 };

// Função para inicializar dados do banco de dados
async function initData() {
    try {
        const [rows] = await pool.query('SELECT `name`, COUNT(*) AS `total` FROM `voting` GROUP BY `name`');
        rows.forEach(row => {
            voteCount[row.name] = row.total;
        });
        console.log('Dados inicializados com sucesso a partir do banco de dados.');
    } catch (error) {
        console.error('Falha ao inicializar dados:', error);
    }
}

// Função para adicionar um novo voto
async function addVote(candidateName) {
    try {
        await pool.query('INSERT INTO voting (name) VALUES (?)', [candidateName]);
        voteCount[candidateName]++;
        return true;
    } catch (error) {
        console.error('Falha ao salvar voto:', error);
        return false;
    }
}

Detalhes das Funções: A função initData() usa uma consulta GROUP BY para contar o total de votos por candidato, carregando os resultados no objeto voteCount. A função addVote() usa uma declaração preparada (?) para prevenir injeção de SQL. Ambas as funções incluem manipulação básica de erros com blocos try-catch.

5. Integrar Funções do Banco de Dados com Socket.io

A integração ocorre dentro do evento de conexão do Socket.io. Quando um cliente se conecta, o servidor envia os dados mais recentes. O servidor também escuta eventos de envio de voto dos clientes. Este padrão garante que todos os clientes recebam atualizações em tempo real.

io.on('connection', async (socket) => {
    console.log('Cliente conectado');

    // Enviar dados atuais para o cliente recém-conectado
    socket.emit('updateData', voteCount);

    // Lidar com evento de voto do cliente
    socket.on('submitVote', async (candidate) => {
        const success = await addVote(candidate);
        if (success) {
            // Transmitir dados atualizados para TODOS os clientes conectados
            io.emit('updateData', voteCount);
        } else {
            socket.emit('voteError', 'Falha ao salvar o voto.');
        }
    });
});

// Inicializar dados quando o servidor inicia
initData();

Fluxo de Eventos: Primeiro, quando um cliente se conecta, o servidor emite um evento updateData com os dados atuais. Segundo, quando um cliente emite um evento submitVote, o servidor o processa no banco de dados. Se bem-sucedido, o servidor transmite a atualização para todos os clientes. Se falhar, apenas o cliente que enviou o voto recebe uma notificação de erro.

6. Otimização e Melhores Práticas para Produção

Para um ambiente de produção, considere otimizações importantes. Primeiro, use variáveis de ambiente para a configuração do banco de dados. Segundo, implemente lógica de repetição para conexões de banco de dados. Terceiro, considere usar cache Redis na frente do MySQL para cenários de tráfego muito alto.

// Exemplo usando variáveis de ambiente com dotenv
require('dotenv').config();
const pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME
});

Além disso, sempre valide a entrada do cliente antes do processamento no banco de dados. Para um aplicativo de votação, certifique-se de que o valor do candidato recebido corresponda a uma opção válida.

Seguindo estas etapas, sua aplicação Node.js em tempo real agora possui armazenamento de dados permanente usando um banco de dados MySQL. A estrutura do código está mais organizada e pronta para desenvolvimento posterior.

Como próximo passo, você pode adicionar recursos como autenticação de usuários, registro em log ou visualização de dados em tempo real mais complexa. Consulte sempre a documentação oficial do Node.js e a documentação do mysql2 para melhores práticas e atualizações. Esta implementação fornece uma base sólida para aplicações em tempo real que exigem persistência de dados.

Últimos artigos