From the last part, we used djoser to build the authentication backend and then we connected the frontend Vue.js application to it.
In this part we’re going to build an API using django rest framework, this API should provide us with endpoints to start new chat sessions, join chat sessions, post new messages and fetch a chat session’s message history.
Architecture
Before we start, let’s discuss how everything works from a higher level
Overview
When a user sends a message, this message would be forwarded to django through the API.
After django has received the message, It would also be forwarded to RabbitMQ.
RabbitMQ uses an exchange to broadcast the messages to multiple queues. The queues are communication channels that would eventually deliver the messages to the clients. The Workers are background processes that do the actual work of broadcasting and delivering messages.
RabbitMQ is the glue that connects two important parts of our application together (Django and uWSGI).
It also makes our application very flexible because aside django and python. They are various means to send messages to RabbitMQ even from the command line!. This means that other applications that have no knowledge of our chat application can still communicate with it.
For example a desktop application written in C# can put a message on a RabbitMQ queue and the message would be recieved by our clients and even a mobile app.
Without RabbitMQ, the uWSGI WebSocket server is dumb and knows nothing about our django app (how to access the database, authentication etc) because it run in a different process or even a different webserver entirely depending on your setup.
uWSGI serves as the websocket server. After the client has established a connection and specified the channel (RabbitMQ exchange) they want to receive messages from. We’ll read the message as soon as they’re received and send them down to the users instantly using the WebSocket.
If you’re worried about having an extra node server. The webpack dev server is just a convenience for development locally when you’re ready to deploy your application you can bundle your application by running:
npm build
The resulting static files can be served by any capable web server E.g Nginx, Apache even github pages. In essence, The Vue layer doesn’t really exist it’s Typically the user’s web browser.
Implementation
In this part, our goal is to implement the API with django rest framework. The API would allow users start new chat sessions, join existing sessions and send messages. It would also allow us retrieve messages from a chat session.
Let’s start a new django app called chat
Make sure you add the new app to the INSTALLED_APPS list before you proceed.
Next we’re going to create models that’ll hold the data for the messages, chat sessions and the associated users. Let’s create some new models in models.py.
Make sure you run the migrations before proceeding so the database tables can be created.
The next step is to create views (API endpoints) that would be used by our Vue app to manipulate data on the server.
We can easily make use of django rest framework to create them (We won’t make use of serializers since our models are pretty simple). Let’s do that now in views.py
The patch method for the ChatSessionView is idempotent because making an request to it multiples times gives us the same result. That means a user can join a chat room several times but there’s only going to be one instance of that user in the response (and also in our database table).
Another thing to note about the patch method is that it returns the owner of the chat room as a member but in our database we never add the owner as a member of the room, we just retrieve his information and insert it into the list that’s returned back to the client. There’s no point duplicating information by having the owner as a member of their chatroom in the database.
We could have easily gotten the user in the patch method by calling request.user instead we got the username from the posted data and used that to get the user. This causes an extra database SELECT but why did we do that?
Let me give you a simple scenario, what happens if we decide to invite our friends by username to a chat session. With request.user we wouldn’t be able to do that because request.user would refer to the current authenticated user making the request.
On the otherhand with username’s it’s a piece of cake we just need to post the username to server and it’ll use that to retrieve the user and add them to the chat room.
Also, if you decide to add an “Invite multiple users” functionality, you can modify the code to read a list of usernames and fetch them in one go from the database. It’s up to you.
Using username’s is makes our code more flexible and open to improvements.
Now let’s add the URL’s for the views.
Don’t forget to include the URL’s in the base urls.py file
Our endpoints are ready and any AUTHENTICATED user make requests to them
Let’s try it out:
Let’s send some messages
Let’s request for the messages history
Congrats! if you made it this far, you’ve succesfully built an API that allows users to communicate with each other by starting chat sessions and inviting other users to join the session.
In the next part, we’ll build the Chat UI and call those methods from Vue.