Course Lessons

JAVASCRIPT FOUNDATIONS + AI MINDSET

Back to Course

Build an AI Chat Assistant in React

JAVASCRIPT... Lesson 25 of 41 9 min

Build an AI Chat Assistant in React

Full‑Stack AI: React + Express + OpenAI

By AI Learning Assistant  ·  React  ·  Express  ·  OpenAI API  ·  Chat History

🤖 YOUR KIND AI LEARNING ASSISTANT

Welcome to Day 25! Today we're building something truly exciting — your own AI chat assistant, powered by OpenAI. You'll connect a React frontend to a Node.js backend, send messages, and get intelligent responses. I'll explain every step, from CORS to conversation history. Don't worry — we'll go slowly. You've got this! 🚀

What We Are Building

A FULL‑STACK AI CHAT APPLICATION

We'll create a chat interface where you can type a question about JavaScript or React, and an AI assistant (powered by OpenAI's GPT) will answer. The app has two parts:

  • Backend (Express server) — Holds your OpenAI API key securely, receives messages from React, calls the OpenAI API, and sends back the AI's reply.
  • Frontend (React app) — Displays the chat UI, sends user messages to the backend, shows typing indicators, and displays the AI's responses.

🎯 WHY TWO SERVERS? (REACT + EXPRESS)

Your API key must stay secret — never put it in React code (anyone can see it!). So we put it in a Node.js backend. The React app (running on port 3000) talks to the backend (port 5000). The backend talks to OpenAI. This is a standard full‑stack pattern.

Step 1 — Building the Backend (server.js)

EXPRESS · CORS · OPENAI SDK · ENVIRONMENT VARIABLES

First, create a new folder for your backend (e.g., `webbo3-ai-backend`). Inside, initialize npm and install dependencies:

npm init -y
npm install express cors openai dotenv

📜 server.js — Complete Backend Code (with line‑by‑line comments)

// ============================================
// AI Chat Backend (Express + OpenAI)
// ============================================

// Load environment variables from .env file (keeps API key secret)
require("dotenv").config();

// Import Express (web framework for Node.js)
const express = require("express");

// CORS allows your React app (port 3000) to talk to this server (port 5000)
// Without CORS, browsers block requests from different origins.
const cors = require("cors");

// OpenAI SDK — official library to call GPT models
const OpenAI = require("openai");

// Create the Express app
const app = express();

// Enable CORS for all routes (simplifies development)
app.use(cors());

// Enable JSON body parsing — so we can read req.body.message
app.use(express.json());

// Initialize OpenAI client with API key from environment variables
const client = new OpenAI({ 
  apiKey: process.env.OPENAI_API_KEY 
});

// ============================================
// POST /chat — receives a user message and returns AI reply
// ============================================
app.post("/chat", async (req, res) => {
  // Extract the message sent from React frontend
  const { message } = req.body;
  
  // Call OpenAI's chat completion API
  const response = await client.chat.completions.create({
    model: "gpt-3.5-turbo",
    messages: [
      {
        // System prompt — gives the AI a personality and role
        role: "system",
        content: "You are Webbo3's AI tutor. Help students learn JavaScript and React. Be encouraging and keep answers short (1‑2 paragraphs)."
      },
      {
        role: "user",
        content: message
      }
    ]
  });
  
  // Extract the reply text from the response
  const reply = response.choices[0].message.content;
  
  // Send the reply back to React as JSON
  res.json({ reply });
});

// Start the server on port 5000
app.listen(5000, () => {
  console.log("AI server running on http://localhost:5000");
});
    

📌 IMPORTANT: CREATE .env FILE

In your backend folder, create a file named .env with your OpenAI API key:
OPENAI_API_KEY=sk‑your‑actual‑key‑here
Then add .env to your .gitignore to keep it secret!

Step 2 — Building the Frontend (React App)

CHAT UI · STATE MANAGEMENT · FETCH TO BACKEND

Now let's create the React chat interface. Create a new React app (or use your existing one) and replace src/App.js with the code below.

📜 src/App.js — Complete React Chat Component

// ============================================
// AI Chat Assistant — React Frontend
// ============================================

import { useState } from "react";

function App() {
  // messages array stores all conversation turns: user messages and assistant replies
  const [messages, setMessages] = useState([
    { role: "assistant", text: "Hi! I am Webbo3 AI. Ask me anything about JavaScript!" }
  ]);
  
  // Current input value (what user is typing)
  const [input, setInput] = useState("");
  
  // Loading state — true while waiting for AI response, shows "thinking..."
  const [loading, setLoading] = useState(false);

  // Called when user clicks Send or presses Enter
  const sendMessage = async () => {
    // Don't send empty messages
    if (!input.trim()) return;
    
    // Save the user's message text before clearing input
    const userMessage = input;
    
    // Add user message to conversation history
    setMessages(prev => [...prev, { role: "user", text: userMessage }]);
    
    // Clear input field
    setInput("");
    
    // Show loading indicator
    setLoading(true);
    
    // Send the user message to our backend (port 5000)
    const res = await fetch("http://localhost:5000/chat", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: userMessage })
    });
    
    // Parse the JSON response from backend
    const data = await res.json();
    
    // Add assistant's reply to conversation history
    setMessages(prev => [...prev, { role: "assistant", text: data.reply }]);
    
    // Hide loading indicator
    setLoading(false);
  };

  // Render the chat UI
  return (
    <div style={{ padding: "40px", maxWidth: "600px", margin: "0 auto" }}>
      <h1>Webbo3 AI Tutorh1>
      
      // Chat message container (scrollable)
      <div style={{
        border: "1px solid #ccc",
        borderRadius: "8px",
        padding: "20px",
        height: "400px",
        overflowY: "scroll",
        marginBottom: "10px"
      }}>
        {messages.map((msg, i) => (
          <div key={i} style={{
            textAlign: msg.role === "user" ? "right" : "left",
            margin: "8px 0"
          }}>
            <span style={{
              background: msg.role === "user" ? "#007bff" : "#f0a500",
              color: "white",
              padding: "8px 14px",
              borderRadius: "20px",
              display: "inline-block"
            }}>
              {msg.text}
            span>
          div>
        ))}
        {loading && <p>Webbo3 AI is thinking...p>}
      div>
      
      // Input area and send button
      <input
        value={input}
        onChange={e => setInput(e.target.value)}
        onKeyPress={e => e.key === "Enter" && sendMessage()}
        placeholder="Ask about JavaScript..."
        style={{ width: "80%", padding: "10px" }}
      />
      <button onClick={sendMessage} style={{
        padding: "10px 20px",
        background: "#007bff",
        color: "white",
        border: "none"
      }}>Sendbutton>
    div>
  );
}

export default App;
    

🎯 Live Simulation — Try the Chat Interface (Demo)

Hi! I am Webbo3 AI. Ask me anything about JavaScript!

✨ This simulates the UI (no real AI). In your real app, it will connect to OpenAI.

💬 DEEP DIVE: CONVERSATION HISTORY THREADING

Why send full history?

🗣️ YOU ASK THE AI:

"What is conversation history threading in AI APIs and why is it important to send the full history in each request? Show me how to maintain context across multiple messages."

🤗 YOUR KIND AI ASSISTANT RESPONDS (Step‑by‑Step):

What is conversation history threading?

OpenAI's API is stateless — it doesn't remember previous messages. Each request is independent. If you only send the latest user message, the AI has no memory of what you said before. To have a real conversation, you must send the entire chat history every time.

Why is it important?

  • Context awareness: The AI needs to remember earlier messages to answer follow‑up questions (e.g., "What about arrays?" after asking about functions).
  • Coherent conversations: Without history, each message is isolated — the AI would answer each question as if it's the first.
  • User experience: Users expect the AI to remember the conversation thread, just like a human.

How to maintain context — two approaches

Approach 1 (as in our current code): Store the entire conversation array in React state. Send only the current user message to the backend, and the backend does not store history — it uses only the single message. This works for simple Q&A but loses context for follow‑ups.

Approach 2 (full history threading): Send the entire conversation array to the backend with each request. The backend includes all previous messages in the OpenAI API call. This gives the AI full memory of the conversation.

Code Example — Maintaining Full History (in backend)

// In React: send the whole messages array
const res = await fetch("/chat", {
  method: "POST",
  body: JSON.stringify({ messages: messages }) // send ALL previous messages
});

// In Express backend:
app.post("/chat", async (req, res) => {
  const { messages } = req.body;  // messages is an array of {role, content}
  const response = await openai.chat.completions.create({
    model: "gpt-3.5-turbo",
    messages: messages  // includes system prompt + full user/assistant history
  });
  res.json({ reply: response.choices[0].message.content });
});

Important limitation: Long conversations can exceed token limits (max ~4096 tokens for gpt-3.5-turbo). For production, you may need to truncate old messages or use a sliding window.

In our current app: We only send the latest message to keep the code simple. For a true conversational AI, you would upgrade to sending the full history. The `messages` array in React already stores everything — you only need to adjust the fetch body to send it all!

📝 YOUR LEARNING JOURNAL

Today I built a full‑stack AI chat app! I learned that the backend hides the API key, CORS allows the frontend to talk to the backend, and conversation history threading is crucial for context. Our current app sends only the latest message, but I now understand how to upgrade it to send the full conversation array. The system prompt gives the AI a teaching personality. I feel like a real full‑stack developer now. Next, I'll add typing indicators and error handling. 🚀

How to Run the Complete App

TERMINAL · TWO SERVERS · TEST

# 1. BACKEND (in webbo3-ai-backend folder)
node server.js
# You should see: AI server running on port 5000

# 2. FRONTEND (in your React app folder, e.g., webbo3-react)
npm start
# React app opens on http://localhost:3000

# 3. Open your browser to http://localhost:3000
# Type a message and watch the AI respond!
  

Quick Reference — Full‑Stack AI Chat

ConceptWhat it does
CORSAllows React app on port 3000 to call backend on port 5000
dotenvLoads API key from .env file (keeps secrets out of code)
System promptGives the AI a personality and rules (e.g., "be encouraging")
Conversation historyArray of messages stored in React state
ThreadingSending full history to maintain context (future improvement)

🤗 YOUR KIND AI ASSISTANT — FINAL TIPS

💬 "My fetch is failing (CORS error)" → Make sure your backend is running on port 5000 and you have `app.use(cors())`.

💬 "How do I add typing animation?" → You can simulate typing by streaming the response or adding a delayed character‑by‑character effect.

💬 "What about pricing?" → GPT‑3.5‑turbo is very cheap (fractions of a cent per message). Set a spending limit in your OpenAI account.

You did it! You built a full‑stack AI chat app — from React frontend to Express backend to OpenAI. This is a portfolio‑worthy project. Be proud of yourself. See you tomorrow! 🌟

AI-Assisted JavaScript Learning · Build an AI Chat Assistant in React · Full‑Stack with OpenAI

Complete this lesson

Mark as complete to track your progress