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:
- Login and sign up
- Create group-chats
- Create one-to-one chats
- Send and receive messages
- Attach images and files
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:
- Python 3.6+
- Node.js
- npm (comes with Node.js)
Table of contents
We’ll use the following steps to complete our chat app:
- Create a FastAPI backend
- Setup a Chat Engine project
- Connect our backend to Chat Engine
- Create a ReactJS frontend
- 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:
- fastapi (our server framework)
- uvicorn (to run our server)
- requests (to make API calls to Chat Engine later)
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:
import requests
- Create a new user in Chat Engine on
/signup/
- Fetch / Auth a user from Chat Engine on
/login/
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!