Messaging System Backend
The app supports real-time messaging, where users can send personal messages to each other, or create group chats to have conversations between multiple team members.
📘 Backend Documentation: Group and Personal Chat System
Overview
This backend supports:
Personal and group messaging
Real-time-like polling with intelligent refresh
Group creation via modal with user search
Chat UI state preservation via query param (
currentChatId
)Profile image, message state (
sent
,delivered
,read
)PostgreSQL ENUMs and relationships via SQLAlchemy
🗂️ Database Models
User
Standard user model with profile data. Fields typically include:
id: Integer, primary key
username: String
first_name: String
last_name: String
image_route: String
MessageState ENUM
msg_state_enum = ENUM('sent', 'delivered', 'read', name='msg_state', create_type=False)
Chat
Represents a personal or group chat.
id: Integer, primary key
is_group: Boolean
created_at: DateTime
ChatUser
Many-to-many link table between Chat
and User
.
id: Integer, primary key
chat_id: ForeignKey(Chat.id)
user_id: ForeignKey(User.id)
Message
id: Integer, primary key
chat_id: ForeignKey(Chat.id)
sender_id: ForeignKey(User.id)
message_cont: Text
state: msg_state_enum
date_sent: DateTime
GroupMeta
(if implemented)
Extra info for group chats:
chat_id: ForeignKey(Chat.id), primary key
name: String
description: String
image_route: String
🔧 Routes and Views
1. POST /send-message
Sends a message to either a personal or group chat.
Requires:
chat-id-field
,message-input
Optional:
is-personal-chat-field
to distinguish chat type
@view_config(route_name='send_message', request_method='POST', renderer='json')
def send_message(request):
verify_session(request)
chat_id = request.POST.get('chat-id-field')
is_personal = request.POST.get('is-personal-chat-field') == 'true'
content = request.POST.get('message-input')
# Validate input, sanitize content, etc.
msg = Message(
chat_id=chat_id,
sender_id=request.user.id,
message_cont=content,
state='sent',
date_sent=datetime.utcnow()
)
DBSession.add(msg)
DBSession.flush()
return {'success': True}
2. GET /get-chat-messages/<chat_id>
Fetch messages from a personal chat.
@view_config(route_name='get_chat_messages', renderer='json')
def get_chat_messages(request):
chat_id = request.matchdict['chat_id']
verify_chat_access(request.user.id, chat_id)
messages = DBSession.query(Message)...filter_by(chat_id=chat_id).order_by(Message.date_sent).all()
return {'messages': serialize_messages(messages)}
3. GET /get-group-chat-messages/<chat_id>
Fetch messages from a group chat + metadata.
@view_config(route_name='get_group_chat_messages', renderer='json')
def get_group_chat_messages(request):
chat_id = request.matchdict['chat_id']
chat = DBSession.query(Chat).get(chat_id)
if not chat or not chat.is_group:
raise HTTPNotFound
verify_chat_access(request.user.id, chat_id)
messages = DBSession.query(Message)...filter_by(chat_id=chat_id).order_by(Message.date_sent).all()
members = DBSession.query(User)...join(ChatUser).filter(ChatUser.chat_id == chat_id).all()
return {
'messages': serialize_group_messages(messages),
'chat_name': chat.groupmeta.name,
'description': chat.groupmeta.description,
'image_route': chat.groupmeta.image_route,
'creation_date': chat.created_at,
'members': serialize_users(members)
}
4. GET /search-global?q=...
Global user search (used by group creation modal)
@view_config(route_name='search_global', renderer='json')
def search_global(request):
q = request.params.get('q', '').strip()
if not q or len(q) < 2:
return []
users = DBSession.query(User)...filter(...ilike(f'%{q}%')).limit(10).all()
return [serialize_user(u) for u in users]
5. POST /create-group-chat
Creates a new group with selected users.
@view_config(route_name='create_group_chat', request_method='POST')
def create_group_chat(request):
user_ids = request.POST.getall('user_ids')
if len(user_ids) < 2:
return HTTPBadRequest
chat = Chat(is_group=True, created_at=datetime.utcnow())
DBSession.add(chat)
DBSession.flush()
for uid in user_ids + [request.user.id]: # Include creator
link = ChatUser(chat_id=chat.id, user_id=uid)
DBSession.add(link)
groupmeta = GroupMeta(
chat_id=chat.id,
name=request.POST.get('group_name') or "Unnamed Group",
description=request.POST.get('group_description') or "",
image_route=request.POST.get('group_image_route') or ""
)
DBSession.add(groupmeta)
return HTTPFound(location=f'/chat?currentChatId={chat.id}')
6. POST /remove-user-from-group
Removes a user from a group. Optionally restrict to group admins.
@view_config(route_name='remove_user_from_group', request_method='POST', renderer='json')
def remove_user_from_group(request):
group_id = request.POST.get('group_id')
target_user_id = request.POST.get('user_id')
# Check if requester is allowed
verify_chat_access(request.user.id, group_id)
DBSession.query(ChatUser).filter_by(chat_id=group_id, user_id=target_user_id).delete()
return {'success': True}
📦 Supporting Utilities
verify_chat_access(user_id, chat_id)
Confirms user belongs to chat (used in all fetch/send routes).
serialize_messages(messages)
Returns message dictionaries with:
[
{
"message_cont": "hi",
"sender_id": 3,
"date_sent": "2025-07-02T13:45:00Z",
"state": "sent"
}
]
serialize_users(users)
Returns minimal public info for chat members.
🛡️ Auth / Session
Use verify_session(request)
on routes to protect endpoints.
This likely wraps cookie/session logic and sets request.user
.
🧠 Notes
Message polling is client-side every ~3.5s, but the backend performs no heavy computation, so it scales acceptably for small/medium apps.
Use
ETag
orLast-Modified
headers in future if backend cache validation is needed.Add pagination or lazy-load if you expect message history to grow large.
Last updated