Vaarta: The Language Exchange App That Outsources the Hard Parts
A clean look at how a niche social network uses MongoDB for identity and relationships, Stream for real-time chat and video, and onboarding rules to keep the match quality high.
- Vaarta feels small only because it refuses to own the whole real-time stack, and that restraint is the source of its clarity.
- The app’s real product is not generic chat, but language-compatible matching shaped by onboarding, profile state, and friend filtering.
- Stream takes over the messy live layer, while MongoDB stays responsible for identity, relationships, and product logic.
- The cleanest UX detail is the call-in-chat pattern, which turns a video action into a durable part of the conversation history.
Why Vaarta Feels Smaller Than It Is
Vaarta looks like a chat app, but that is only the surface. The interesting choice is architectural: it keeps durable product state in MongoDB and pushes real-time chat and video into Stream. That makes the repo feel more focused than a full-stack communication product usually does.
A real-time chat application built with React, Node.js, and Socket.io.
The Product Is Really a Matching Engine
Vaarta is not trying to be Slack or Discord. It is closer to a language-exchange filter with messaging attached. Native language, learning language, and onboarding state shape who shows up, which means the social graph is already opinionated before anyone sends a message.
| Layer | What Vaarta controls | What it avoids owning |
|---|---|---|
| Identity and discovery | MongoDB users, onboarding, friend requests, recommendation filtering | Generic chat infrastructure |
| Live communication | Authentication and capability issuance | WebSocket plumbing, call signaling, media transport |
| Product surface | Language matching and relationship rules | Trying to compete as a broad social network |
How the App Decides Who You Can Meet
The routing logic is strict for a reason. If a user is not authenticated, they do not get into the app. If they are authenticated but not onboarded, they are pushed into onboarding instead of a half-finished social feed. That keeps the network from filling with empty profiles and weak matches.
The recommendation query reinforces the same idea. Vaarta filters out the current user, existing friends, and anyone who has not finished onboarding. In other words, the app is not optimizing for volume. It is optimizing for relevance.
| Signal | Effect on discovery | Why it matters |
|---|---|---|
| isOnboarded | Only complete profiles can appear | Keeps the feed from becoming noisy |
| nativeLanguage + learningLanguage | Shapes who is a good match | Turns profile data into product logic |
| existing friends exclusion | Prevents duplicate recommendations | Keeps the graph clean |
| authenticated session | Blocks anonymous access to core features | Protects the social layer |
What Stream Takes Off Vaarta’s Plate
This is the repo’s biggest leverage point. The backend does not try to become a communications engine. It validates the user, issues a Stream token, and gets out of the way. The frontend waits for that token, then initializes Stream’s chat and video SDKs.
That matters because real-time systems are expensive in hidden ways. Presence, retries, message ordering, call setup, and media transport all add complexity fast. Vaarta avoids that tax by treating live communication as a capability it can broker, not an infrastructure layer it has to own.
const { data: token } = useQuery({
queryKey: ['streamToken'],
queryFn: async () => {
const res = await axiosInstance.get('/chat/token');
return res.data.token;
},
enabled: !!authUser,
});
if (!token) return null;
const chatClient = StreamChat.getInstance(STREAM_API_KEY);
await chatClient.connectUser(currentUser, token);
Why the Chat-to-Call Flow Is Clever
Vaarta does not treat a video call like a separate event that floats outside the conversation. It writes the call invite into the chat itself. That turns the call into a durable artifact, which is much better than forcing both users to remember what happened in another window.
That design also makes the product feel more asynchronous. If the other person is not ready, the invite still lives in the thread. The message history becomes the handoff point, not just a log of old text.
The Friend Request Model Keeps the Social Graph Clean
The repo uses a dedicated FriendRequest collection instead of hiding pending relationships inside user arrays. That is the right move. Pending and accepted are different states, and they deserve a separate model with a clear lifecycle.
When a request is accepted, Vaarta uses atomic updates to add each user to the other’s friends list without duplication. It is a small implementation detail, but it shows discipline. The graph stays consistent because the write path is explicit.
| Approach | Strength | Weakness |
|---|---|---|
| FriendRequest collection | Clear pending and accepted states | More moving parts in the data model |
| Embedded pending arrays | Fewer collections | Harder to reason about lifecycle and duplicates |
| Atomic $addToSet updates | Prevents duplicate friendships | Requires careful backend discipline |
What Vaarta Gets Right, and What Will Break First
Vaarta’s strongest trait is focus. It knows what kind of product it is, and it keeps the code aligned with that answer. The onboarding gate, recommendation query, and Stream handoff all reinforce the same thesis: this is a language exchange app first, a chat app second.
The obvious trade-off is dependency. Stream buys speed and simplicity, but it also creates vendor lock-in and cost sensitivity. The other likely pressure point is scale. The matching query will need pagination and more explicit performance work as the graph grows.
| Choice | Wins | Trade-off |
|---|---|---|
| Use Stream for live features | Faster shipping, less protocol work | Vendor dependence and cost |
| Keep MongoDB for identity | Simple ownership of product state | Backend will need stronger scaling patterns later |
| Restrict access with onboarding | Better match quality | Less casual discovery |