This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
Mini Slack v2.0 is a real-time team messaging platform with channels, private DMs, threaded replies, emoji reactions, typing indicators and unread badges, in a dark/light UI. It also has a "Summarize thread" button that uses a local open-source LLM (via Ollama) to turn a long thread into a short summary.
A naive chat app broadcasts every event to every connected user and re-renders the whole message list on each new message. That works with five users and falls apart as the workspace grows. So I took the slow and steady route and made the boring parts efficient:
-
Targeted WebSocket delivery. Clients send a
SUBSCRIBEevent when they switch channels, and the server keeps achannel_id → usersregistry. A channel message goes only to that channel's subscribers, and a DM goes to exactly two people. - Composite and partial Postgres indexes for the queries a chat app runs constantly: the channel feed, DM pairs, thread replies and unread counts.
-
A leaner React render path.
React.memoonMessageItemplus stableuseCallbackhandlers, so one incoming message re-renders one component instead of the whole visible list. - Local AI summaries. Nobody has to scroll through 40 replies to find out what a thread decided.
Anyone who keeps a team chat open all day gets to be a little lazier: no refreshing, no polling, and no reading every reply to catch up on a thread.
Demo
- 🌐 Live app: https://slack-r7df.vercel.app/
- 📖 API docs (FastAPI/OpenAPI): https://slack-7yia.onrender.com/docs
Code
Mini Slack v2.0 — Real-Time Team Messaging Platform
A production-grade, real-time team collaboration platform built with React, FastAPI, WebSockets, and Neon PostgreSQL — featuring channel-room targeted delivery, composite DB indexing, and an optimised React rendering pipeline.
🌐 Live Web Application: https://slack-r7df.vercel.app/
⚡ Backend REST & WS API: https://slack-7yia.onrender.com
📖 API Documentation: https://slack-7yia.onrender.com/docs
📸 Overview
Mini Slack is a full-stack Slack clone with isolated private DMs, emoji reactions, threaded replies, real-time WebSocket messaging, and a beautiful dark/light mode UI. Built with a clean separation of concerns across backend services, routers, schemas, and ORM models.
✨ Features
💬 Messaging
| Feature | Details |
|---|---|
| Real-time Channel Messaging | Native FastAPI WebSockets — no polling |
| Isolated Private Direct Messages | DMs stored with recipient_id, never appear in channel streams |
| Threaded Message Replies | Sliding side drawer — replies stored as parent_id foreign keys |
| Soft Message Deletion |
is_deleted=True flag preserves thread continuity |
| Emoji |
How I Built It
Backend: FastAPI (Python 3.13), SQLAlchemy 2.x, Pydantic v2, Uvicorn, and native FastAPI WebSockets. Data lives in serverless Neon PostgreSQL. The code is split into routers, services, schemas and ORM models, and the schema auto-migrates on startup. It also seeds 8 engineering channels (#general, #engineering, #web, #blockchain, #ai-ml, #devops, #design, #random).
Frontend: React 18 + Vite, TailwindCSS, Framer Motion, Axios, Lucide icons, emoji-picker-react, and DiceBear avatars.
Design decisions worth calling out:
-
Soft deletes. Deleting a message sets
is_deleted=Trueinstead of removing the row, so threads don't lose their parent. -
DM isolation. DMs are stored with a
recipient_idand filtered out of channel queries, so they can never leak into a channel feed. -
Partial indexes. Each index only covers the rows its query touches, e.g. the channel feed index is restricted to
parent_id IS NULL AND recipient_id IS NULL.
Open-source AI: Ollama. I added a "Summarize thread" button to the thread drawer. When you click it, the FastAPI backend collects the replies for that message, sends them to a locally running open-weight model through Ollama (I used [MODEL NAME]), and returns a short summary to the UI.
-
New endpoint:
POST /api/v1/messages/{id}/summarize. It reuses the same reply query the thread drawer already uses. -
Local inference: the backend calls Ollama's HTTP API on
localhost:11434. No API key, no per-token billing, and nothing leaves the machine. - Failure handling: if Ollama isn't running, the endpoint returns a clear error and the button is disabled, so the rest of the chat is unaffected.
Why Does Open Innovation Matter?
Chat messages are some of the most private data an app handles. Because the model is open-weight and runs locally through Ollama, summarizing a thread doesn't mean shipping my users' conversations to a third-party API. I can pick the model, swap it for a smaller one on weak hardware, and run it with no usage costs. A closed API would have meant sending every thread off-machine, paying per call, and depending on someone else's pricing and uptime.
The rest of the stack is open source too: FastAPI, SQLAlchemy, PostgreSQL, React, Vite and Tailwind. As a first-year student I could read the source whenever something behaved strangely, and that's a big part of how a single person can build a real-time, multi-user app end to end.
The Lazy Payoff
Every feature in Mini Slack removes a small chore from a day of team chat:
- No refreshing. Messages arrive over WebSockets, so there's no polling and no reload to see what's new.
-
No scanning for what's unread. Per-channel unread badges come from a
message_readstable, so you can see which channels need you at a glance. - No hunting for old messages. Workspace-wide search covers channels and messages.
- No losing context. Replies live in a sliding thread drawer, so side conversations stay out of the main feed.
- No noisy channels. Events go only to people subscribed to that channel, so you aren't pulled into conversations that aren't yours. A DM from someone while you're in another channel shows up as a slide-in toast instead.
-
No rebuilding your place. The workspace persists across reloads, and the theme choice is saved in
localStorage. - No reading 40 replies to catch up. The "Summarize thread" button gives you the gist from a local model, with nothing sent to a third party.
The same idea applies on the engineering side. Targeted fan-out and memoized components mean one new message re-renders one component instead of the whole visible list, so the app stays responsive however many messages are on screen.
Prize Categories
[OPTIONAL: list every partner category you're entering, from the challenge page. Otherwise delete this section.]
Top comments (0)