Javascript

Deploy: do código ao ar Já leu

18 min de leitura

Deploy: do código ao ar
Você sabe construir aplicações. Mas construir é apenas metade do trabalho — a outra metade é colocar no ar para que outras pessoas possam usar. Deploy é o processo de levar o código do seu computador para um servidor ace

Você sabe construir aplicações. Mas construir é apenas metade do trabalho — a outra metade é colocar no ar para que outras pessoas possam usar. Deploy é o processo de levar o código do seu computador para um servidor acessível pela internet.

Muitos desenvolvedores excelentes na escrita de código travam quando precisam fazer deploy. Isso acontece porque deploy envolve um conjunto diferente de conceitos — servidores, variáveis de ambiente, builds, domínios, certificados SSL. Este artigo desmistifica tudo isso.

Vamos cobrir deploy do front-end React e do back-end Node.js, usando as plataformas mais usadas pelo mercado em 2025.


O que acontece em um deploy

Antes de qualquer comando, é importante entender conceitualmente o que estamos fazendo. O processo é sempre o mesmo, independente da plataforma.

Para o front-end React, o código precisa ser compilado — o Vite transforma JSX, importações e otimizações em arquivos HTML, CSS e JavaScript puros que qualquer navegador entende. Esses arquivos estáticos são então colocados em um servidor que os entrega ao navegador quando alguém acessa a URL.

Para o back-end Node.js, o processo é diferente — o código não é compilado para o usuário final, mas precisa rodar em um servidor com Node instalado, conectado ao banco de dados, com as variáveis de ambiente configuradas e disponível 24 horas por dia.

Desenvolvimento                    Produção
────────────────────────           ────────────────────────────────
npm run dev                        npm run build → arquivos estáticos
localhost:5173                     CDN global (Vercel, Netlify)
Hot reload automático              Arquivos servidos ao edge

node src/index.js                  Processo gerenciado (PM2, Railway)
localhost:3000                     Servidor com IP público
.env local                         Variáveis de ambiente na plataforma
MongoDB local                      MongoDB Atlas (nuvem)

Deploy do front-end — Vercel

A Vercel é a plataforma mais popular para deploy de aplicações React e foi criada pelos autores do Next.js. O deploy é tão simples que parece mágica — você conecta o repositório do GitHub e a Vercel cuida de todo o resto.

Preparando o projeto React para produção

Antes do deploy, precisamos garantir que o projeto está configurado corretamente. O passo mais importante é usar variáveis de ambiente para a URL da API — nunca escreva URLs hardcoded no código.

# Variáveis de ambiente no Vite usam o prefixo VITE_
# Apenas variáveis com este prefixo ficam acessíveis no código do navegador
# (por segurança — não queremos expor segredos de servidor no front-end)

# .env.development — usado em npm run dev
VITE_API_URL=http://localhost:3000

# .env.production — usado em npm run build
VITE_API_URL=https://sua-api.railway.app
// src/services/api.js
// import.meta.env é a forma do Vite acessar variáveis de ambiente
// Em desenvolvimento: http://localhost:3000
// Em produção: https://sua-api.railway.app
const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';

export async function api(endpoint, opcoes = {}) {
  // ... resto da implementação
}
// vite.config.js
// Configurações importantes para produção
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],

  build: {
    // Divide o bundle em chunks menores para carregamento mais rápido
    // Cada rota lazy() vira um arquivo separado
    rollupOptions: {
      output: {
        // Agrupa bibliotecas grandes em um chunk separado
        // Isso melhora o cache — react e react-dom raramente mudam
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          query: ['@tanstack/react-query'],
        },
      },
    },
    // Avisa quando algum chunk ultrapassar 500KB
    chunkSizeWarningLimit: 500,
  },
});

Para SPAs com React Router, precisamos de um arquivo especial que instrui a Vercel a sempre servir o index.html, independente da rota acessada. Sem isso, acessar diretamente /produtos/42 retornaria um 404.

// vercel.json — na raiz do projeto
{
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/index.html"
    }
  ]
}

Deploy na Vercel passo a passo

# Opção 1 — via CLI (mais rápido para testar)
npm install -g vercel
vercel login
vercel          # deploy para preview
vercel --prod   # deploy para produção

# Opção 2 — via GitHub (recomendado para projetos contínuos)
# 1. Suba o código para o GitHub
# 2. Acesse vercel.com e clique em "New Project"
# 3. Importe o repositório
# 4. Configure as variáveis de ambiente na interface:
#    VITE_API_URL = https://sua-api.railway.app
# 5. Clique em Deploy
#
# A partir daí, cada push para main faz deploy automático
# Pull Requests ganham URLs de preview automáticas

Deploy do front-end — Netlify

A Netlify é outra excelente opção, especialmente popular para projetos open source. O processo é similar à Vercel.

# netlify.toml — na raiz do projeto
[build]
  # Comando que gera os arquivos de produção
  command = "npm run build"
  # Pasta com os arquivos gerados pelo build
  publish = "dist"

# Redirect para SPA — sem isso, rotas diretas retornam 404
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200
# Via CLI
npm install -g netlify-cli
netlify login
netlify deploy --build          # preview
netlify deploy --build --prod   # produção

Build local — sempre teste antes do deploy

Um erro comum é fazer push e descobrir que o build de produção falha por algo que funcionava em desenvolvimento. Desenvolva o hábito de testar o build localmente antes de enviar.

# Gera os arquivos de produção na pasta dist/
npm run build

# Serve os arquivos de produção localmente
# Isso simula exatamente como vai funcionar no servidor
npm run preview

# Abra http://localhost:4173 e teste tudo:
# - navegação entre rotas
# - login e autenticação
# - chamadas à API
# - lazy loading das páginas

Deploy do back-end — Railway

O Railway é a plataforma mais simples para fazer deploy de aplicações Node.js com banco de dados. Ele detecta automaticamente que é um projeto Node e configura tudo.

Preparando a API para produção

O primeiro passo é garantir que a aplicação lê corretamente as variáveis de ambiente e que não há nada hardcoded.

// src/config/index.js
// Centraliza toda a configuração em um lugar
// Se alguma variável obrigatória estiver faltando, a aplicação
// para imediatamente com uma mensagem clara — melhor do que falhar silenciosamente
require('dotenv').config();

const config = {
  porta: Number(process.env.PORT) || 3000,
  ambiente: process.env.NODE_ENV || 'development',
  mongoUrl: process.env.MONGODB_URL,
  jwtSecret: process.env.JWT_SECRET,
  jwtExpiracao: process.env.JWT_EXPIRA_EM || '7d',

  // Helpers para verificar o ambiente atual
  eDev: process.env.NODE_ENV === 'development',
  eProd: process.env.NODE_ENV === 'production',
  eTeste: process.env.NODE_ENV === 'test',
};

// Valida variáveis obrigatórias no startup
// Em produção, se MONGODB_URL não existir, é melhor parar agora
// do que falhar misteriosamente na primeira requisição ao banco
const obrigatorias = ['MONGODB_URL', 'JWT_SECRET'];
const faltando = obrigatorias.filter((v) => !process.env[v]);

if (faltando.length > 0 && config.eProd) {
  console.error(
    `[Config] Variáveis de ambiente obrigatórias faltando: ${faltando.join(', ')}`
  );
  process.exit(1);
}

module.exports = config;
// src/index.js
// Ajustes importantes para produção
const express = require('express');
const cors = require('cors');
const config = require('./config');
const { conectar } = require('./config/database');

const app = express();

// CORS configurado para aceitar apenas origens conhecidas em produção
// Em desenvolvimento, aceita qualquer origem para facilitar os testes
const origemPermitida = config.eProd
  ? process.env.FRONTEND_URL  // Ex: https://meuapp.vercel.app
  : '*';

app.use(
  cors({
    origin: origemPermitida,
    methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
  })
);

app.use(express.json({ limit: '10mb' }));

// Health check — endpoint simples que o Railway e outros serviços
// usam para saber se a aplicação está viva
// Se retornar 200, o serviço está ok. Se não responder, reinicia o processo.
app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    ambiente: config.ambiente,
    uptime: Math.floor(process.uptime()) + 's',
    timestamp: new Date().toISOString(),
  });
});

// Rotas da API
app.use('/auth', require('./routes/auth'));
app.use('/produtos', require('./routes/produtos'));
app.use('/tarefas', require('./routes/tarefas'));

// Middlewares de erro
app.use(require('./middlewares/erros').naoEncontrado);
app.use(require('./middlewares/erros').tratadorDeErros);

// Inicialização
conectar().then(() => {
  app.listen(config.porta, () => {
    console.log(`[Server] Rodando na porta ${config.porta} (${config.ambiente})`);
  });
});

// Encerramento gracioso — finaliza requisições em andamento antes de parar
// O Railway envia SIGTERM antes de reiniciar o container
process.on('SIGTERM', () => {
  console.log('[Server] SIGTERM recebido. Encerrando...');
  process.exit(0);
});

module.exports = app;
// package.json — scripts importantes para produção
{
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}

O campo engines instrui o Railway (e outras plataformas) sobre qual versão do Node usar. Sempre especifique.

Deploy no Railway

# Via CLI
npm install -g @railway/cli
railway login
railway init          # inicializa o projeto no Railway
railway up            # faz o deploy

# Configurando variáveis de ambiente via CLI
railway variables set NODE_ENV=production
railway variables set JWT_SECRET=sua-chave-super-secreta-aqui
railway variables set MONGODB_URL=mongodb+srv://...
railway variables set FRONTEND_URL=https://seuapp.vercel.app

Também é possível configurar tudo pela interface web do Railway, que é mais visual e recomendada para iniciantes.


MongoDB Atlas — banco de dados em nuvem

Em produção, não podemos usar o MongoDB local. O MongoDB Atlas é o serviço de nuvem oficial do MongoDB com um tier gratuito generoso.

Criando um cluster no Atlas:
1. Acesse cloud.mongodb.com e crie uma conta
2. Crie um novo projeto e um cluster (M0 Free é suficiente para começar)
3. Em "Database Access": crie um usuário com senha forte
4. Em "Network Access": adicione 0.0.0.0/0 (aceita conexão de qualquer IP)
   — Em produção real, você restringiria ao IP do seu servidor
5. Em "Connect": escolha "Connect your application"
6. Copie a connection string:
   mongodb+srv://usuario:senha@cluster.mongodb.net/nomebanco

Esta string vai para a variável de ambiente MONGODB_URL no Railway.
// src/config/database.js
// Configuração de conexão otimizada para produção
const mongoose = require('mongoose');

async function conectar() {
  const url = process.env.MONGODB_URL;

  if (!url) {
    throw new Error('MONGODB_URL não configurada.');
  }

  try {
    await mongoose.connect(url, {
      // Timeout para encontrar um servidor disponível no cluster
      serverSelectionTimeoutMS: 10000,

      // Timeout para operações individuais no banco
      socketTimeoutMS: 45000,
    });

    console.log(`[DB] Conectado: ${mongoose.connection.host}`);

    // Eventos de conexão para monitoramento em produção
    mongoose.connection.on('disconnected', () => {
      console.warn('[DB] Desconectado do MongoDB.');
    });

    mongoose.connection.on('error', (erro) => {
      console.error('[DB] Erro de conexão:', erro.message);
    });

    // Reconecta automaticamente se a conexão cair
    mongoose.connection.on('reconnected', () => {
      console.info('[DB] Reconectado ao MongoDB.');
    });
  } catch (erro) {
    console.error('[DB] Falha ao conectar:', erro.message);
    throw erro;
  }
}

module.exports = { conectar };

Variáveis de ambiente — organização e segurança

Um dos erros mais comuns e perigosos em deploy é vazar secrets. Estas são as regras invioláveis.

# ✅ O que SEMPRE vai no .gitignore
.env
.env.local
.env.production
.env.*.local

# ✅ O que vai no repositório (sem valores reais)
.env.example

# .env.example — template documentado para o time
# Cada desenvolvedor copia este arquivo para .env e preenche os valores
NODE_ENV=development
PORT=3000
MONGODB_URL=mongodb://localhost:27017/meuapp
JWT_SECRET=          # gere com: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_EXPIRA_EM=7d
FRONTEND_URL=http://localhost:5173
# Gerando um JWT_SECRET seguro no terminal
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
# Saída: a4f8c2e9b1d7... (128 caracteres hexadecimais)
# Use este valor como JWT_SECRET — nunca use "segredo123" em produção

Pipeline de CI/CD com GitHub Actions

CI/CD automatiza o processo de teste e deploy. Toda vez que você faz push para main, o GitHub Actions executa os testes e, se passarem, faz o deploy automaticamente. Isso elimina deploys manuais e garante que código com testes falhando nunca vai para produção.

# .github/workflows/deploy.yml
# Este arquivo define o pipeline de CI/CD

name: Testes e Deploy

# Dispara quando há push para a branch main
on:
  push:
    branches: [main]
  # Também roda em Pull Requests — mas sem o step de deploy
  pull_request:
    branches: [main]

jobs:
  # Job 1: roda os testes
  testar:
    runs-on: ubuntu-latest
    name: Testes automatizados

    steps:
      # Faz checkout do código
      - uses: actions/checkout@v4

      # Configura Node.js na versão especificada
      - name: Configurar Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'    # cacheia node_modules entre execuções

      # Instala dependências com versões exatas do lockfile
      - name: Instalar dependências
        run: npm ci

      # Verifica formatação e lint
      - name: Verificar qualidade de código
        run: npm run quality

      # Roda os testes com relatório de cobertura
      - name: Executar testes
        run: npm run test:coverage
        env:
          # Variáveis de ambiente para os testes
          # Secrets são configurados no GitHub: Settings → Secrets
          MONGODB_TEST_URL: ${{ secrets.MONGODB_TEST_URL }}
          JWT_SECRET: test-secret-apenas-para-testes

  # Job 2: deploy (só roda se os testes passarem e for push para main)
  deploy:
    needs: testar           # aguarda o job "testar" completar com sucesso
    runs-on: ubuntu-latest
    # Só faz deploy em push direto para main (não em Pull Requests)
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    name: Deploy para produção

    steps:
      - uses: actions/checkout@v4

      - name: Configurar Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Instalar dependências
        run: npm ci

      # Deploy no Railway usando o token de autenticação
      # RAILWAY_TOKEN é configurado em GitHub Secrets
      - name: Deploy no Railway
        run: |
          npm install -g @railway/cli
          railway up --service ${{ secrets.RAILWAY_SERVICE_ID }}
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

Para o front-end no Vercel, o deploy é ainda mais simples — a própria Vercel detecta novos commits no GitHub e faz deploy automaticamente, sem precisar de GitHub Actions.


Monitorando a aplicação em produção

Depois do deploy, você precisa saber se tudo está funcionando. Há três tipos de monitoramento essenciais.

Logs mostram o que está acontecendo em tempo real. O Railway e o Vercel têm dashboards de log integrados.

Uptime monitoring avisa quando sua aplicação cai. O UptimeRobot (gratuito) faz pings no seu /health a cada 5 minutos e manda email se não responder.

Error tracking captura exceções com contexto — qual usuário estava, qual ação executou, qual linha do código falhou. O Sentry tem um plano gratuito excelente.

// Integrando Sentry no back-end Node.js
// npm install @sentry/node

const Sentry = require('@sentry/node');

// Inicializa ANTES de qualquer coisa
Sentry.init({
  // DSN vem das configurações do projeto no sentry.io
  dsn: process.env.SENTRY_DSN,

  // Captura 10% das transações para não estourar a cota gratuita
  tracesSampleRate: 0.1,

  // Não captura erros em desenvolvimento
  enabled: process.env.NODE_ENV === 'production',
});

// No error handler do Express — Sentry precisa vir ANTES do seu handler
app.use(Sentry.Handlers.errorHandler());
app.use(tratadorDeErros); // seu handler customizado

Checklist de deploy

Antes de cada deploy em produção, percorra este checklist mentalmente. Ele foi construído a partir de erros reais — cada item representa algo que já deu errado em algum projeto.

Front-end (React)
─────────────────────────────────────────────────────────
[ ] npm run build executa sem erros
[ ] npm run preview funciona corretamente
[ ] VITE_API_URL aponta para a API de produção
[ ] vercel.json ou netlify.toml com redirect configurado
[ ] Nenhuma URL de localhost hardcoded no código
[ ] Console.log de debug removidos (ou só em dev)
[ ] Lazy loading em todas as páginas grandes

Back-end (Node.js)
─────────────────────────────────────────────────────────
[ ] Todas as configurações leem de process.env
[ ] .env.example atualizado com novas variáveis
[ ] .env nunca commitado (está no .gitignore)
[ ] JWT_SECRET é uma string longa e aleatória
[ ] MONGODB_URL aponta para o Atlas (não para localhost)
[ ] CORS configurado com a URL do front-end de produção
[ ] NODE_ENV=production nas variáveis de ambiente
[ ] Campo "engines" no package.json com versão do Node
[ ] Endpoint /health respondendo 200
[ ] Testes passando (npm test)

Banco de dados (MongoDB Atlas)
─────────────────────────────────────────────────────────
[ ] Cluster criado e rodando
[ ] Usuário do banco criado com senha forte
[ ] Network Access configurado
[ ] Connection string testada localmente antes do deploy

Geral
─────────────────────────────────────────────────────────
[ ] Domínio customizado configurado (opcional)
[ ] HTTPS ativo (Vercel e Railway fazem isso automaticamente)
[ ] Uptime monitor configurado
[ ] Sentry ou similar para error tracking

Domínio customizado

Após o deploy, você recebe uma URL gerada automaticamente como meuapp.vercel.app. Para usar um domínio próprio como meuapp.com.br, o processo é simples.

1. Compre o domínio em um registrador (Registro.br, GoDaddy, Namecheap)

2. No painel da Vercel:
   Settings → Domains → Add → "meuapp.com.br"
   A Vercel mostrará os registros DNS que você precisa configurar

3. No painel do seu registrador de domínio:
   Adicione os registros DNS fornecidos pela Vercel:
   Tipo: A       Nome: @       Valor: 76.76.21.21
   Tipo: CNAME   Nome: www     Valor: cname.vercel-dns.com

4. Aguarde a propagação DNS (pode levar de minutos a 24 horas)

5. HTTPS é configurado automaticamente pela Vercel via Let's Encrypt

Tarefa para você

Faça o deploy completo da SPA construída no Módulo 6:

# Back-end
# 1. Crie um cluster gratuito no MongoDB Atlas
#    e configure a connection string

# 2. Crie uma conta no Railway e faça deploy da API
#    Configurar variáveis de ambiente:
#    NODE_ENV, PORT, MONGODB_URL, JWT_SECRET, FRONTEND_URL

# 3. Teste o endpoint /health da API em produção:
#    curl https://sua-api.railway.app/health

# Front-end
# 4. Configure o arquivo vercel.json para SPA routing

# 5. Crie uma conta na Vercel e importe o repositório
#    Configurar variável:
#    VITE_API_URL = https://sua-api.railway.app

# 6. Faça login e crie um produto pela URL de produção

# CI/CD
# 7. Configure o GitHub Actions com o workflow do artigo
#    Adicione os secrets no GitHub:
#    RAILWAY_TOKEN, RAILWAY_SERVICE_ID, MONGODB_TEST_URL

# 8. Faça um commit e observe o pipeline rodando:
#    testes → qualidade de código → deploy automático

# Monitoramento
# 9. Crie uma conta gratuita no UptimeRobot
#    Monitore o endpoint /health a cada 5 minutos

# 10. Compartilhe a URL da aplicação funcionando em produção!

Conclusão

Neste artigo você aprendeu:

  • A diferença entre deploy de front-end (arquivos estáticos) e back-end (processo Node.js)
  • Como preparar o projeto React para produção com variáveis de ambiente e code splitting
  • Deploy no Vercel com configuração para SPA routing
  • Deploy no Netlify como alternativa
  • Como preparar a API Node.js para produção com CORS, health check e configuração centralizada
  • Deploy no Railway para aplicações Node.js
  • MongoDB Atlas para banco de dados em nuvem
  • Boas práticas de segurança para variáveis de ambiente
  • Pipeline de CI/CD com GitHub Actions — testes automáticos antes de cada deploy
  • Monitoramento com logs, uptime monitoring e error tracking
  • Checklist completo de deploy para não esquecer nada
  • Como configurar domínio customizado

No próximo artigo vamos aprender sobre segurança em aplicações web — as vulnerabilidades mais comuns e como proteger sua aplicação contra elas.


 

📚 Fontes e Referências

  • Vercel — Documentação: https://vercel.com/docs
  • Netlify — Documentação: https://docs.netlify.com
  • Railway — Documentação: https://docs.railway.app
  • MongoDB Atlas: https://www.mongodb.com/atlas
  • GitHub Actions — Documentação: https://docs.github.com/en/actions
  • Sentry — Node.js SDK: https://docs.sentry.io/platforms/node
  • UptimeRobot: https://uptimerobot.com
  • Let's Encrypt: https://letsencrypt.org
  • The DevOps Handbook — Gene Kim et al. (IT Revolution Press)
  • roadmap.sh — DevOps: https://roadmap.sh/devops
Comentários

Mais em Javascript

O que é JavaScript e por que aprender?
O que é JavaScript e por que aprender?

Se você quer trabalhar com desenvolvimento web — seja criando sit...

O que é o DOM e como o JavaScript interage com o HTML
O que é o DOM e como o JavaScript interage com o HTML

Até agora todo o nosso código rodou no console — um ambie...

Tratamento de erros em requisições HTTP
Tratamento de erros em requisições HTTP

Fazer uma requisição funcionar no ambiente de desenvolvimento é fácil. O verd...