Chat Engine logo Chat Engine
FastAPI and ReactJS chat app with Chat Engine.

Build a full-stack chat app with FastAPI, ReactJS, and Chat Engine

In this tutorial, we will be building a real-time chat application using FastAPI, ReactJS, and Chat Engine.

FastAPI is a modern, fast web framework for building APIs with Python 3.6+. ReactJS is a JavaScript library for building user interfaces. Chat Engine is a free chat toolkit for building chat apps in React.

As you can see in the demo above, we’ll build a fullstack chat experience where you can:

We’ll be leveraging Chat Engine’s APIs and components to save us time and effort.

(You can also watch this tutorial on YouTube here)

Prerequisites

Before we start, make sure you have the following tools installed on your machine:

Table of contents

We’ll use the following steps to complete our chat app:

  1. Create a FastAPI backend
  2. Setup a Chat Engine project
  3. Connect our backend to Chat Engine
  4. Create a ReactJS frontend
  5. Connect our frontend to FastAPI and Chat Engine

These steps take a total of 10 minutes and ~175 lines of code to complete. You can copy and paste most of the code!

1. Create a FastAPI backend

Our project will have two folders: A FastAPI backend/ and a ReactJS frontend/. Let’s create our project and backend folder now.

mkdir fastapi-reactjs-chat
cd fastapi-reactjs-chat
mkdir backend
cd backend

Within our backend folder, let’s setup a python3 virutal environment to hold all our dependencies.

python3 -m venv venv
source venv/bin/activate

Let’s install the following dependencies:

pip install --upgrade pip
pip install fastapi "uvicorn[standard]" requests
pip freeze > requirements.txt

Finally, let’s create a backend/main.py file to host our login() and signup() methods.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from pydantic import BaseModel
from typing import Union

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class User(BaseModel):
    username: str
    secret: str
    email: Union[str, None] = None
    first_name: Union[str, None] = None
    last_name: Union[str, None] = None

@app.post('/login/')
async def login(user: User):
    return {}

@app.post('/signup/')
async def signup(user: User):
    return {}

We can run this server with the following line: uvicorn main:app --reload --port 3001

Part one is complete! We now have a FastAPI server, running at http://localhost:3001, with two routes for /login/ and /signup/.

Your folder structure should look like this:

.
└── backend
    ├── main.py
    ├── requirements.txt
    └── venv

The server currently does nothing. Let’s setup and connect Chat Engine so we can give users access to Chat Engine’s chat APIs.

2. Setup a Chat Engine project

This part is simple. Go to ChatEngine.io, create an account, and setup a “New Project”.

Your new “project” is a chat server, provided for free by Chat Engine! This chat server supports API calls and websocket connections - so your users can authenticate into it and host chatrooms there.

Copy the Project ID and Private Key. The Project ID identifies your project in API calls. The Private Key allows you to create and destroy users with your API calls.

Now we’ll connect our FastAPI backend to Chat Engine and fully complete the server.

3. Connect our backend to Chat Engine

To connect FastAPI to Chat Engine, we’ll do the following:

We can do this with the create user API and the authenticate user API. Both of which (and all APIs) are documented at rest.chatengine.io.

We’ll use the requests library to complete these steps.

Copy/paste the following code into backend/main.py to do this. (Replace PROJECT_ID and PRIVATE_KEY with your own project’s values.)

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from pydantic import BaseModel
from typing import Union

import requests

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

PROJECT_ID = "..."
PRIVATE_KEY = "..."

class User(BaseModel):
    username: str
    secret: str
    email: Union[str, None] = None
    first_name: Union[str, None] = None
    last_name: Union[str, None] = None

@app.post('/login/')
async def login(user: User):
    response = requests.get('https://api.chatengine.io/users/me/',
        headers={
            "Project-ID": PROJECT_ID,
            "User-Name": user.username,
            "User-Secret": user.secret
        }
    )
    return response.json()

@app.post('/signup/')
async def signup(user: User):
    response = requests.post('https://api.chatengine.io/users/',
        data={
            "username": user.username,
            "secret": user.secret,
            "email": user.email,
            "first_name": user.first_name,
            "last_name": user.last_name,
        },
        headers={ "Private-Key": PRIVATE_KEY }
    )
    return response.json()

Done! Now when you call /signup/ a user is created on Chat Engine. When you call /login/ a user’s credentials are authenticated on Chat Engine!

Now that our users have access to our Chat Engine project / server, let’s setup our ReactJS frontend and connect it to FastAPI and Chat Engine!

4. Create a ReactJS frontend

At the fastapi-reactjs-chat level of your project, run:

npx create-react-app frontend
cd frontend

5. Connect our frontend to FastAPI and Chat Engine

We’ll need to setup a login/sign-up page and a chats page for authenticated users. To do so, let’s add the following code to frontend/src/app.js

import { useState } from "react";

import AuthPage from "./authPage";
import ChatsPage from "./chatsPage";

function App() {
  const [user, setUser] = useState();

  if (!user) {
    return <AuthPage onAuth={(user) => setUser(user)} />;
  } else {
    return <ChatsPage user={user} />;
  }
}

export default App;

This code will render an authPage or chatsPage depending on if an authenticated user is in our app state. Clearly, we have not created authPage or chatsPage.

Create a frontend/src/authPage.js file and add the following code:

import { useState } from "react";
import axios from "axios";

const AuthPage = (props) => {
  const [username, setUsername] = useState();
  const [secret, setSecret] = useState();
  const [email, setEmail] = useState();
  const [first_name, setFirstName] = useState();
  const [last_name, setLastName] = useState();

  const onLogin = (e) => {
    e.preventDefault();
    axios
      .post("http://localhost:3001/login", { username, secret })
      .then((r) => props.onAuth({ ...r.data, secret })) // NOTE: over-ride secret
      .catch((e) => console.log(JSON.stringify(e.response.data)));
  };

  const onSignup = (e) => {
    e.preventDefault();
    axios
      .post("http://localhost:3001/signup", {
        username,
        secret,
        email,
        first_name,
        last_name,
      })
      .then((r) => props.onAuth({ ...r.data, secret })) // NOTE: over-ride secret
      .catch((e) => console.log(JSON.stringify(e.response.data)));
  };

  return (
    <div className="login-page">
      <div className="card">
        {/* Login Form */}
        <form onSubmit={onLogin}>
          <div className="title">Login</div>
          <input
            type="text"
            name="username"
            placeholder="Username"
            onChange={(e) => setUsername(e.target.value)}
          />
          <input
            type="password"
            name="secret"
            placeholder="Password"
            onChange={(e) => setSecret(e.target.value)}
          />
          <button type="submit">LOG IN</button>
        </form>

        {/* Sign Up Form */}
        <form onSubmit={onSignup}>
          <div className="title">or Sign Up</div>
          <input
            type="text"
            name="username"
            placeholder="Username"
            onChange={(e) => setUsername(e.target.value)}
          />
          <input
            type="password"
            name="secret"
            placeholder="Password"
            onChange={(e) => setSecret(e.target.value)}
          />
          <input
            type="text"
            name="email"
            placeholder="Email"
            onChange={(e) => setEmail(e.target.value)}
          />
          <input
            type="text"
            name="first_name"
            placeholder="First name"
            onChange={(e) => setFirstName(e.target.value)}
          />
          <input
            type="text"
            name="last_name"
            placeholder="Last name"
            onChange={(e) => setLastName(e.target.value)}
          />
          <button type="submit">SIGN UP</button>
        </form>
      </div>

      <style>{`
      .login-page { width: 100vw; height: 100vh; padding-top: 6vw; background: linear-gradient(180deg, rgba(117,84,160,1) 7%, rgba(117,84,160,1) 17%, rgba(106,95,168,1) 29%, rgba(99,103,174,1) 44%, rgba(87,116,184,1) 66%, rgba(70,135,198,1) 83%, rgba(44,163,219,1) 96%, rgba(22,188,237,1) 100%, rgba(0,212,255,1) 100%); }
      .card { width: 200px; position: relative; left: calc(50vw - 100px); text-align: center; }
      .title { padding-top: 32px; font-size: 22px; color: white; font-weight: 700; }
      input { width: calc(100% - 16px); margin-top: 12px; padding: 8px; background-color: #e6f7ff; outline: none; border: 1px solid #e6f7ff; }
      button { margin-top: 12px; width: 100%; padding: 8px; }
      `}</style>
    </div>
  );
};

export default AuthPage;

This is a single ReactJS page with a Login and SignUp form. Once completed, they call http://localhost:3001/login and http://localhost:3001/signup respectively.

Assuming our server is running, these API calls will fetch and create new users, then return the proper JSON data. Once the API call is complete, our props.onAuth callback will update user state in src/app.js. This means we’re ready to work on out chats page.

Finally, to complete our chatsPage, we’ll need to connect our authenticated users to our Chat Engine server. We can do this with one of Chat Engine’s prebuilt UIs.

In this tutorial, we’ll use react-chat-engine-pretty.

Run the follwoing bash command and add the following code to frontend/src/chatsPage.js.

npm install react-chat-engine-pretty
import { PrettyChatWindow } from "react-chat-engine-pretty";

const ChatsPage = (props) => {
  return (
    <div style={{ height: "100vh" }}>
      <PrettyChatWindow
        projectId="..."
        username={props.user.username} // adam
        secret={props.user.secret} // pass1234
        style={{ height: "100%" }}
      />
    </div>
  );
};

export default ChatsPage;

Make sure you add your “Project ID” to the projectId prop.

What you’re doing here is connecting a user to your chat engine project with a prebuilt UI. react-chat-engine-pretty handles all state management and websocket connections! This way your chat app is fully functional with little-to-no coding!

Run the react app with npm run start

Done 🎉

You’re chat app is fully functional now! Please email adam@lamorre.co with any questions or post in Chat Engine’s stackoverflow.

Hope you enjoyed!