Construindo um Chatbot

Frontend + IA

Interface mínima de chat — campo, envio, loading e exibição de respostas.

Intermediário 20 min 25 pontos Leitura 0%

Nesta aula você vai

  • Montar UI de chat sem biblioteca pesada
  • Enviar mensagem via fetch ao backend
  • Tratar estados loading, erro e empty

Frontend + IA

Objetivos

  • Criar interface mínima que stakeholders entendem
  • Nunca chamar API do LLM direto do browser

HTML + CSS mínimo

<div id="chat">
  <div id="messages" aria-live="polite"></div>
  <form id="form">
    <input id="input" type="text" placeholder="Digite sua pergunta..." autocomplete="off" />
    <button type="submit">Enviar</button>
  </form>
</div>

JavaScript (cliente)

const messagesEl = document.getElementById('messages');
const form = document.getElementById('form');
const input = document.getElementById('input');

function append(role, text) {
  const div = document.createElement('div');
  div.className = `msg msg--${role}`;
  div.textContent = text;
  messagesEl.appendChild(div);
  messagesEl.scrollTop = messagesEl.scrollHeight;
}

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const text = input.value.trim();
  if (!text) return;

  input.value = '';
  append('user', text);
  append('assistant', '...'); // placeholder loading

  try {
    const res = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: text }),
    });
    const data = await res.json();
    messagesEl.lastChild.textContent = data.reply ?? data.error;
  } catch {
    messagesEl.lastChild.textContent = 'Erro de conexão. Tente novamente.';
  }
});

Estados obrigatórios

Estado UX
Idle Input habilitado
Loading Botão disabled, indicador visual
Error Mensagem clara, input reabilitado
Empty Sugestão: "Pergunte sobre pedidos ou produtos"

Segurança no frontend

  • Sanitize exibição (textContent, não innerHTML com resposta da IA)
  • Limite tamanho do input (ex: 2.000 chars)
  • CSRF token se usar cookies de sessão

React/Vue — mesmo princípio

const [messages, setMessages] = useState([]);
const [loading, setLoading] = useState(false);

async function send(text) {
  setMessages(m => [...m, { role: 'user', content: text }]);
  setLoading(true);
  try {
    const { reply } = await fetch('/api/chat', { ... }).then(r => r.json());
    setMessages(m => [...m, { role: 'assistant', content: reply }]);
  } finally {
    setLoading(false);
  }
}

Resumo

  • UI simples: lista + input + submit
  • Frontend → seu /api/chat, não OpenAI
  • Loading e erro são parte do produto, não detalhe
  • textContent / escape para evitar XSS na resposta do modelo