Introduction
socketio
is an event driven library that allows for real-time, bi-directional communication between server and client. It is mostly used for chat applications but for my project, I used it to replace the HTTP server client communication.
For my phase 5 project blog, I decided to share how to implementsocketio
with myfull stack application
My goal with usingsocketio
was to replace the communication between the frontend and backend but keep as much of my project logic intact and the same. This means replacing all fetch requests (GET, POST, PATCH, and DELETE) and API routes but still keep how my project processes that data on the frontend and backend.
Setting upsocketio
Backend
Flask was a requirement for my phase 5 project so I used the 'flask-socketio' package. I am using a python virtual shell to install my packages, so I ran the following command in terminal:pyenv install flask-socketio
On the server side, you instantiate your flask app and configure it as you normally do, but withflask-socketio
you need to do the following additional things:
- You need to edit your CORS and set
resources={r"/*":{"origins":"*"}}
- You need to insatiate a
socketio
object, pass your app into it, and set the CORS settings - Instead of running
app.run(port=5555, debug=True)
you instead runsocketio.run(app, debug=True, port=5555)
# config filefrom flask import Flaskfrom flask_socketio import SocketIOfrom flask_cors import CORS# Instantiate appapp = Flask(__name__)# Instantiate CORSCORS(app, resources={r"/*":{"origins":"*"}})socketio = SocketIO(app, cors_allowed_origins="*")# app fileif __name__ == '__main__': socketio.run(app, debug=True, port=5555)
Now you have asocketio
server running on the backend
Frontend
Let’s set up the frontend. Install the package,npm install socket.io-client
Now to establish your frontend connection, you instantiate asocketio
object
const socket = io("localhost:5555", {transports: ["websocket"], cors: { origin: "*",},}
There are some settings to pass:
- The first is the domain to connect to. We're passing our local host backend address here
- Second, we're passing a dictionary of settings
socketio
uses both websockets and http connections, where the latter is a backup. to ensure my project uses websockets, I configured it to only establish that connection. this is optional / dependent on what your project goals are- cors: setting the cors settings
Note that with each instance of the socket object, a new connection is established. I wanted to have one connection so I created this socket in theApp
component as a global variable. I then used React context to pass this object directly to child components that receive and send data.
Start your react app and thesocketio
connection is now establish.
Testing Your Connection
To ensure and test that your connection works, lets include the following
On the backendapp.py
file:
@socketio.on("connect")def handle_connect(): print("Client connected!") emit( "connected", {"data": f"id: {request.sid} is connected"})
socketio
for python and flask uses decorators to register events.
One special event is called "connect". Callingsocketio.on("connect")
event decorator wrapped around a function will call that function every time a first connection is established.
Here, every time we connect on the backend, we print out to the terminal "client connected".
Next weemit
data back.
emit
is asocketio
function that sends data to the name of the room you are sending that data to. The next argument is the data you're sending.socketio
JSONIFY your data automatically so you do not need to use that function.
So here, we are sending data to the frontend to a room called "connected". Please note, do not use special key word events such as connect or disconnect as it will cause bugs to occur.
On the frontend,
socket.on("connected", (data)=>{ console.log(data) })
Thesocket.on
is similar to the backend where the first argument is the name of the room. However, this uses a second argument as a callback function, where the data is passed and you can perform whichever action you want. In our case, we receive the socket id and it gets printed out in the console log. This will indicate to us that the connection was successfully established
Converting my application
In theApp
component, a fetch get request is performed within theuseEffect
hook to get the data from the backend. I replaced these fetch requests as follows:
original:
fetch("/workout_plans") .then( r => r.json()) .then( d => setPlans(d)) fetch("/schedules") .then( r => r.json()) .then( d => setSchClasses(d)) fetch("/coaches") .then( r => r.json()) .then( d => setCoaches(d)) fetch("/exercise_moves") .then( r => r.json()) .then( d => setMoves(d))
to:
socket.on("coaches", data => setCoaches(data))
socket.on("schedules", data => setSchClasses(data))
socket.on("workout_plans", data => setPlans(data))
socket.on("exercise_moves", data => setMoves(data))
socketio
is listening to these four rooms for data.
On the backend. I created a function that pulls all of my data from the database. Note, the objects from my DB needs to be serialized. I usedflask-marshamallow
andmarshamallow
to serialize these DB objects and include or exclude specific relationships and fields. if your data from the backend doesn't have complex relationships that require some type of serialization, you may pass them directly intosocketio
.
The data is then emitted to the rooms I specified:
def refresh_all_data(): coaches = Coach.query.all() workout_plans = Workout_Plan.query.all() exercise_moves = Exercise_Move.query.all() schedules = Schedule.query.all() emit("coaches", coaches_schema.dump(coaches)) emit("workout_plans", workout_plans_schema.dump(workout_plans)) emit("exercise_moves", exercise_moves_schema.dump(exercise_moves)) emit("schedules", schedules_schema.dump(schedules))
Thus, when the frontend and backend first connect, within the on "connect" event, I call on thisrefresh_all_data()
function. All of the records are pulled, the data is serialized, and it is then emitted to these rooms.
On the frontend, these rooms are being listened to, and when the data is received, the object data is passed to the state variables established, updating the frontend's various views. Since these rooms are within theuseEffect
hook, React re-renders again but not infinitely. Everything else in theApp
component stays the same.
Next, all components with the suffixForm
utilize fetch requests for patching, or posting data. Also within theClassScheduleDetail
component, there are fetching delete requests. These components logic are dependent on acknowledgements, andsocketio
allows you to provide this when data is emitted and received.
UtilizingCoachForm
as an example, previously when a form data is submitted, there were two routes: if the form was submitting a new object or updating an existing object. Within each of those two routes, if the response was ok, to perform various actions involving refreshing the component and setting the page to show the object or to display the error from the backend as to why it didn't work.
Previously:
function submitData(values){ if (values.id === ""){ fetch("/coaches", { method: "POST", headers: {"Content-Type" : "application/json"}, body: JSON.stringify(values) }) .then( r => { if (r.ok){ r.json().then(data => { setRefresh(!refresh) history.push(`/coaches/${data.id}`) setFormData(data) setApiError({}) }) } else { r.json().then( err => { setApiError(err) }) } }) } else { fetch(`${values.id}`, { method : "PATCH", headers : { "Content-Type" : "application/json"}, body : JSON.stringify(values) }) .then( r => { if (r.ok){ r.json().then(data => { setRefresh(!refresh) setApiError({}) }) } else { r.json().then(err => { setApiError(err)}) } }) } }
All of my components that processed data like this all follow a very similar logic pattern. I replaced these fetch requests with the following on the frontend:
function submitData(values){ if (values.id === ""){ socket.emit("new_coach", values, result => { if (result.ok){ setRefresh(!refresh) history.push(`/coaches/${result.data.id}`) setFormData(result.data) setApiError({}) } else { setApiError(result.errors) } }) } else { socket.emit("update_coach", values, result => { if (result.ok){ setRefresh(!refresh) history.push(`/coaches/${result.data.id}`) setFormData(result.data) setApiError({}) } else { setApiError(result.errors) } }) }}
Above, thesocket.emit()
function has three arguments: the name of the room, the data we are transmitting, and an optional acknowledgement. The acknowledgement we have to design and set up on the backend so that my app maintains the same code logic as before.
On the backend, I originally had API routes to handle get / post requests and get / patch / delete requests. Usingsocketio
, I removed the following:
class CoachesIndex(Resource): def get(self): coaches = Coach.query.all() response = make_response( coaches_schema.dump(coaches), 200 ) return response def post(self): ch_data = request.get_json() del ch_data["id"] try: new_coach = Coach(**ch_data) db.session.add(new_coach) db.session.commit() except Exception as e: error_message = str(e) return {"errors" : error_message }, 400 response = make_response( coach_schema.dump(new_coach), 201 ) return response
I register events that correspond to the frontend's emitting response, one for new objects being created, and another for objects being updated. For the coach path I created the following:
@socketio.on("new_coach")def handle_new_coach(data): result = { "data" : None, "errors" : {}, "ok" : False } del data["id"] try: new_coach = Coach(**data) db.session.add(new_coach) db.session.commit() except Exception as e: error_message = str(e) result["errors"] = error_message return result result["ok"] = True result["data"] = coach_schema.dump(new_coach) refresh_all_data() return result
The data from the frontend can be received as a parameter within your function whereas before the data is received from the request object and pulled usingget_json()
function. There are some other changes as well:
- I create a result dictionary. here I mimic some of the response attributes the frontend is dependent on.
- if creating and submitting the data was successful to the DB, I call on that
refresh_all_data()
function. When it runs, it emits data to the rooms I specified however, nothing happens yet... - I return the result dictionary. In socketio for python, acknowledgements is provided through
return
s at the end of yoursocketio.on
function
On the frontend, the component reads if the response is ok. This works similarly to previous way where I cause a change on the dependency array that theuseEffect
utilizes (refresh variable). This causes the code within it to run, allowing the rooms to be read, and for my app to update.
Thank you for reading my blog. While not the most common use ofsocketio
, implementing this technology was fun and interesting. I hope others who are on this journey can utilize some of the things I learned.
Top comments(2)

- LocationGreece
- WorkSoftware author at my desk
- Joined
It's always useful to read implementations of applications for learning and own practice evaluation purposes. Best experience I had in nodejs with socket.io was when using rethinkdb, as it has its own functions that can be used to emit updates when data change is detected. Too bad it's an abandoned anymore db.

Hi,
This is Sourov Pal. I am a freelance web developer and Software Developer. I can do one of project for free. If you like my work you will pay me otherwise you don't need to pay. No upfront needed, no contract needed. If you want to outsource your work to me you may knock me.
My what's app no is: +8801919852044
Github Profile:github.com/sourovpal
Thanks
For further actions, you may consider blocking this person and/orreporting abuse