Cursos

Como conectar um app do Lovable a uma API local com Node.js

Continue o protótipo da barbearia: crie uma API local com serviços, horários e agendamentos fictícios, conecte o formulário e teste respostas de sucesso e erro.

Por Equipe Central Stack30/10/202612 minGuia prático
LovableNode.jsExpressAPICORSprojeto para iniciantes

Compartilhar

Envie este conteúdo para sua rede ou salve o link para publicar depois.

Notebook exibindo um programa de desenvolvimento, imagem ilustrativa para conectar uma interface a uma API.
Foto: Rodrigo Santos / Pexels

Da escolha guardada no navegador a uma API

Na etapa anterior, o app demonstrativo da barbearia passou a guardar serviço e horário no localStorage. Agora vamos dar o próximo passo: fazer a interface conversar com uma API local feita com Node.js e Express. A API vai listar serviços e horários fictícios e aceitar um agendamento de demonstração.

Este exercício não cria reservas reais nem salva dados em um banco. Os registros ficam apenas na memória do processo e desaparecem quando o servidor é reiniciado. Não inclua nome, telefone, e-mail, senha ou informação de clientes.

Prepare o projeto para rodar localmente

O preview hospedado do Lovable não consegue chamar automaticamente um servidor que está rodando apenas no seu computador. Para este teste, baixe ou sincronize o projeto e execute a interface localmente. Assim, a aplicação web pode conversar com a API local em outra porta.

No terminal, entre na pasta principal do app e crie uma pasta separada para a API. Os comandos abaixo inicializam um pacote Node e instalam somente Express e o middleware CORS. Se o seu projeto já tem uma pasta de servidor, adapte o caminho em vez de criar outra estrutura.

Terminal, na pasta principal do projeto
mkdir api
cd api
npm init -y
npm install express cors
Estrutura sugerida
barbearia-demo/
  api/
    server.js
    package.json
  src/
    lib/
      api.js

Crie endpoints com dados inventados

Crie api/server.js. O servidor abaixo expõe serviços, horários disponíveis para uma data válida e uma rota POST para receber uma escolha. O CORS permite apenas o endereço local padrão do Vite. Se o Vite iniciar em outra porta, ajuste a origem para a que aparece no terminal.

api/server.js
const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors({ origin: 'http://localhost:5173' }));
app.use(express.json());

const servicos = [
  { id: 'corte', nome: 'Corte' },
  { id: 'barba', nome: 'Barba' },
];
const horarios = ['09:00', '10:00', '11:00'];
const agendamentos = [];

function dataValida(valor) {
  if (typeof valor !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(valor)) {
    return false;
  }

  const data = new Date(`${valor}T00:00:00Z`);
  return !Number.isNaN(data.valueOf()) && data.toISOString().slice(0, 10) === valor;
}

app.get('/api/servicos', (_req, res) => {
  res.json(servicos);
});

app.get('/api/horarios', (req, res) => {
  const { data } = req.query;
  if (!dataValida(data)) {
    return res.status(400).json({ message: 'Informe uma data válida no formato AAAA-MM-DD.' });
  }

  const ocupados = agendamentos
    .filter((item) => item.data === data)
    .map((item) => item.horario);

  return res.json({ data, horarios: horarios.filter((hora) => !ocupados.includes(hora)) });
});

app.post('/api/agendamentos', (req, res) => {
  const { servicoId, data, horario } = req.body ?? {};
  const servicoExiste = servicos.some((item) => item.id === servicoId);

  if (!servicoExiste || !dataValida(data) || !horarios.includes(horario)) {
    return res.status(400).json({ message: 'Confira serviço, data e horário.' });
  }

  const horarioOcupado = agendamentos.some(
    (item) => item.data === data && item.horario === horario,
  );
  if (horarioOcupado) {
    return res.status(409).json({ message: 'Esse horário já foi usado nesta demonstração.' });
  }

  const agendamento = { id: agendamentos.length + 1, servicoId, data, horario };
  agendamentos.push(agendamento);
  return res.status(201).json({ agendamento });
});

app.listen(3001, () => {
  console.log('API de demonstração disponível em http://localhost:3001');
});

Ligue a interface à rota de agendamento

Na interface, mantenha os controles que já existem e envie apenas os identificadores fictícios. Uma função pequena centraliza a URL, verifica o status HTTP e transforma respostas de erro em mensagens que a tela pode mostrar.

src/lib/api.js
const API_URL = 'http://localhost:3001/api';

export async function criarAgendamento(escolha) {
  const resposta = await fetch(`${API_URL}/agendamentos`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(escolha),
  });

  const resultado = await resposta.json();
  if (!resposta.ok) {
    throw new Error(resultado.message || 'Não foi possível enviar a demonstração.');
  }

  return resultado.agendamento;
}

Chame criarAgendamento com servicoId, data e horario a partir do formulário. Enquanto a Promise está pendente, desabilite o botão e mostre um estado de carregamento; use try/catch para exibir a mensagem retornada pela API. A propriedade ok precisa ser conferida porque fetch não rejeita automaticamente uma resposta HTTP 400 ou 409.

Peça uma integração limitada ao Lovable

Se a ferramenta ajudar a adaptar a tela, peça que ela inspecione os componentes existentes antes de mudar qualquer arquivo. O projeto precisa manter a aparência e exibir claramente carregamento, confirmação e erro, sem fingir que há uma reserva real.

Prompt para integrar o formulário
Inspecione o app fictício da barbearia e conecte o formulário existente à API local em http://localhost:3001/api. Use GET /servicos para listar opções, GET /horarios?data=AAAA-MM-DD para consultar horários e POST /agendamentos para enviar servicoId, data e horario.

Mantenha o layout atual. Mostre carregamento, sucesso, validação e erros HTTP; impeça duplo envio enquanto a chamada estiver pendente. Use somente nomes de campos fictícios já existentes. Não adicione nome, telefone, e-mail, login, banco de dados, serviço hospedado ou integração de pagamento. Não publique o app. Antes de editar, liste os arquivos que pretende alterar; no final, explique como testar cada estado.

Execute e teste os dois processos

Abra dois terminais na pasta principal do projeto. No primeiro, inicie a API. No segundo, inicie a interface Vite. Se a ferramenta mostrar outra porta para o front-end, atualize a origem autorizada em cors antes de testar.

Terminal 1, servidor da API
cd api
node server.js
Terminal 2, interface local
npm run dev
  • Abra a URL local exibida pelo Vite e confira se os serviços aparecem no formulário.
  • Envie uma combinação fictícia de serviço, data e horário. A tela deve mostrar confirmação e a resposta da API deve ter status 201.
  • Tente enviar a mesma data e horário de novo. A API deve responder 409 e a interface deve explicar o conflito.
  • Pare e reinicie o servidor. Os agendamentos de demonstração somem porque estão apenas na memória.
  • Confira o console do navegador se aparecer erro de rede ou CORS; verifique as portas e a origem permitida no servidor.

Limites do protótipo e próximo passo

A API local não tem autenticação, banco de dados, proteção contra concorrência nem armazenamento permanente. O exemplo serve para aprender o caminho entre interface e servidor, não para receber reservas de clientes. Em um produto real, seria necessário projetar validação no servidor, persistência, autorização, proteção de dados e deploy seguro; também seria preciso restringir CORS aos domínios usados.

Para aprofundar as rotas e respostas HTTP, consulte o tutorial de API com Node.js. Antes de qualquer integração real, faça um plano de segurança e privacidade apropriado para os dados envolvidos.

Fontes utilizadas