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.

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.
Como fazer seu app com IA lembrar as escolhas usando localStorage
Revise a versão que guarda escolhas fictícias no navegador antes de conectá-la a um servidor.
Como criar uma API com Node.js do zero
Consulte o tutorial de programação se quiser revisar a estrutura de uma API antes do exercício aplicado.
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.
mkdir api
cd api
npm init -y
npm install express corsbarbearia-demo/
api/
server.js
package.json
src/
lib/
api.jsCrie 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.
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.
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.
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.
cd api
node server.jsnpm 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.
