In this tutorial, we will dive into the world of Event-Driven Architecture (EDA) using React and FastAPI to build a full-stack application. Event-Driven Architecture is a design pattern that promotes the production, detection, consumption, and reaction to events happening within a system. In this tutorial, we will build a simple chat application to showcase the power of EDA.
Prerequisites:
- Basic understanding of React and FastAPI
- Node.js installed on your machine
- Python installed on your machine
- Visual Studio Code or any code editor of your choice
Let’s get started by setting up our project!
- Setting up the backend with FastAPI:
First, create a new directory for your project and navigate to that directory in your terminal. Then, run the following commands to set up a new Python virtual environment and install FastAPI.
python -m venv venv
source venv/bin/activate
pip install fastapi uvicorn
Next, create a new file named main.py
and add the following code to create a simple FastAPI server.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
To run the FastAPI server, use the following command in your terminal:
uvicorn main:app --reload
You should now be able to access your FastAPI server at http://localhost:8000.
- Setting up the frontend with React:
Now, let’s set up the frontend of our application using React. In your project directory, run the following commands to create a new React app using Create React App.
npx create-react-app frontend
cd frontend
npm start
This will start a development server for your React app at http://localhost:3000.
- Building the chat application:
In our chat application, we will showcase how EDA can be used to send and receive messages in real-time.
First, let’s add a textbox and a button to our React app to send messages. Open the App.js
file in the src
directory and modify it as follows:
import React, { useState } from 'react';
function App() {
const [message, setMessage] = useState('');
const handleMessageChange = (e) => {
setMessage(e.target.value);
}
const sendMessage = () => {
// Add code to send message
}
return (
<div>
<input type="text" value={message} onChange={handleMessageChange} />
<button onClick={sendMessage}>Send</button>
</div>
);
}
export default App;
Next, we need to send the message inputted by the user to our FastAPI server. We will use Axios, a promise-based HTTP client for the browser and node.js, to make API requests. Install Axios by running the following command in your frontend directory:
npm install axios
Now, modify the sendMessage
function in App.js
to send the message to the FastAPI server when the user clicks the Send button:
import axios from 'axios';
const sendMessage = () => {
axios.post('http://localhost:8000/send-message', { message })
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
}
- Implementing Event-Driven Architecture:
To implement Event-Driven Architecture in our chat application, we will use a message broker called RabbitMQ. RabbitMQ is a popular open-source message broker that supports multiple messaging protocols.
First, install RabbitMQ on your machine by following the instructions on the official website: https://www.rabbitmq.com/download.html
Next, we need a library to interact with RabbitMQ in our FastAPI server. Install the pika
library by running the following command in your backend directory:
pip install pika
Now, modify the main.py
file in your backend directory to handle incoming messages from the frontend and publish them to RabbitMQ:
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='messages')
@app.post("/send-message")
async def send_message(message: str):
channel.basic_publish(exchange='', routing_key='messages', body=message)
return {"message": "Message sent"}
In this code, we establish a connection to RabbitMQ and declare a queue named messages
. When a message is received at the /send-message
endpoint, it is published to the messages
queue in RabbitMQ.
- Consuming messages from RabbitMQ:
Now, let’s consume the messages published to themessages
queue in RabbitMQ and send them to the frontend in real-time. Modify themain.py
file in your backend directory as follows:
from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='messages')
for method_frame, properties, body in channel.consume('messages', inactivity_timeout=1):
if method_frame:
await websocket.send_text(body.decode())
connection.close()
In this code, we establish a WebSocket connection with the frontend and consume messages from the messages
queue in RabbitMQ. As soon as a message is received, we send it to the frontend using the WebSocket connection.
- Connecting the frontend to the WebSocket:
Finally, we need to connect the frontend to the WebSocket endpoint in our FastAPI server to receive messages in real-time. Modify theApp.js
file in your frontend directory as follows:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
const handleMessageChange = (e) => {
setMessage(e.target.value);
}
const sendMessage = () => {
axios.post('http://localhost:8000/send-message', { message })
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
}
useEffect(() => {
const socket = new WebSocket('ws://localhost:8000/ws');
socket.onmessage = (event) => {
setMessages([...messages, event.data]);
}
return () => {
socket.close();
}
}, []);
return (
<div>
<input type="text" value={message} onChange={handleMessageChange} />
<button onClick={sendMessage}>Send</button>
<ul>
{messages.map((msg, index) => (
<li key={index}>{msg}</li>
))}
</ul>
</div>
);
}
export default App;
In this code, we establish a WebSocket connection with the /ws
endpoint in our FastAPI server. Whenever a message is received, we update the messages
state in our React app to display the message in real-time.
And that’s it! You have successfully built a chat application using Event-Driven Architecture with React and FastAPI. This tutorial covered the basics of EDA and how it can be implemented in a full-stack application. Feel free to further enhance the app with more features and functionalities using EDA. Happy coding!
I learned a lot, thanks for the class !
Thank you,this EDA illustration with the cloud DB and Python was 10/10,highly grateful ,may the Lord Jesus Christ bless you for all the hard work you put in.
nice tutorial, big thx !!
Why pass the CREATE_DELIVERY type in the HTTP call since the URL used is deliveries/create ?
Can't the type be simply defined the the app.post(deliveries/create) endpoint ?
I don't get why this would be the client that define the event type.
this tutorial could be brilliantly explained for dumb newbies like me, but thanks anyway, it's free after all
This rocks!
This won't work by default because there's no value set to Redis so why bother?
Poor content and not enough explanation. For instance, at 1:06:22 he deletes code and copy-pastes other, without explanation of the philosophy.
using postman with fastapi 🤨🫣
Seems you've forgotten to push commits of the frontend code repo.
Thanks Antonio. Took me most of a day to complete but learnt a ton. Big thanks!
Despite the economic downturn, I'm happy ☺️. I have been earning $60,200 returns from my $10,000 investment every 13 days
31:30 As we modifying a resource , "dispatch" method should be a "put" method?
why I got TypeError: unsupported operand type(s) for |: 'dict' and 'dict' when trying to update the status?
So I understand the need for recreating the delivery’s current state from event history, but how important is it in this case? Since the database is Redis, wouldn’t a failure wipe both the event history AND the delivery state history?
In a real-world application, would the events be backed up somewhere else? I’m picturing the build_state function pulling from a separate, slower database.
I guess redis cloud is handling the backups, but then it seems like we’re expecting redis to lose deliveries but not events for some reason
Can you tell me about the dart programming language? Plzz guys 🙏
You can make a video exploring GDevelop and Explaining everything about that.. Please?
Please make a full angular course
9:57 decoding is not need, Decode from UTF-8 by default is set (true is the default)
Angular will be awesome