Back to Blog
React NativeExpoSocket.ioMongoDBMobileReal-time

Building Eşlik Et: An Events Companion App With Expo and Socket.io

Umut Korkmaz2026-05-307 min read

For a while I wanted to build something that was less about content and more about getting people into the same room. Eşlik Et, which means roughly to accompany or be a companion in Turkish, is an events companion app. The idea is simple to describe and surprisingly hard to do well: you find an event you want to attend, and the app helps you find someone to attend it with. Concerts, matches, exhibitions, a film you do not want to see alone. The hard part was never the screens. It was the trust, the timing, and the real-time plumbing that makes two strangers feel comfortable enough to actually show up.

The app replaced an older Laravel site I had built years earlier. That version worked, but it was a web app pretending to be a social product, and the mismatch showed. People wanted notifications, location, and the kind of immediacy you only get from a real mobile app. So I rebuilt it properly.

The shape of the system

Eşlik Et is an Expo React Native app shipping to both iOS and Android under the bundle id com.esliket.esliket. Behind it sits an Express and TypeScript API backed by MongoDB, with Socket.io handling all the real-time messaging. There is a separate React and Vite admin panel for moderation and operations, and a self-hosted Expo Updates server for delivering over-the-air updates. Four moving parts, each with a clear job.

I deliberately kept the mobile app and the API as separate concerns. The app owns presentation, navigation, and device integrations. The API owns truth: who matched with whom, which messages exist, what an event actually is. This split sounds obvious, but it pays off the moment you have two clients, because the admin panel and the mobile app talk to the same API and see the same data. There is no second source of truth to keep in sync.

Real-time matching and messaging

The interesting engineering lives in Socket.io. When two people express interest in attending the same event, the matching logic runs server-side and, if it connects them, both clients learn about it instantly. From there, messaging is a live channel rather than a request-response loop. Typing indicators, delivery, and presence all flow over the socket, while MongoDB stores the durable record so nothing is lost if a connection drops.

What I learned is that real-time is mostly about the unhappy paths. Reconnection after a tunnel or an elevator. A message sent while the socket was briefly down. A user who backgrounded the app mid-conversation. I spent more time on acknowledgements and reconnection state than on the happy path of two people chatting. If you treat the socket as reliable, you will ship a product that feels broken to anyone on a real mobile network. So I treated it as unreliable by default and reconciled against the database as the authority.

The device integrations that make it feel native

A social app lives or dies on the small native touches. Eşlik Et has Google and Apple sign-in, because asking someone to invent another password before they have any reason to trust you is a quiet way to lose them. There are maps and location so events have a real place attached and people can gauge how far they are willing to travel. There are in-app purchases for the premium tier, and push notifications to bring people back when something actually happens, like a match or a new message.

Expo carried a lot of this. The managed workflow let me reach for native modules without living inside Xcode and Android Studio every day, which kept me focused on the product instead of build configuration. It is not free of friction, and there are moments where you do drop into native territory, but for a solo build the leverage is real.

Notifications that respect the person

Push notifications are the easiest feature to abuse. The temptation, especially with retention pressure, is to ping people constantly. I tried hard to do the opposite. A notification should mean something happened that you asked to know about: someone wants to attend with you, someone replied, an event you saved is near. Nothing else. No manufactured urgency, no fake activity to drag you back.

The reason is partly ethical and partly practical. A social app that cries wolf trains people to ignore it, and a muted app is a dead app. Restraint here is not a nicety. It is retention strategy that happens to also be honest.

Trust and moderation are the actual product

The feature I underestimated most was moderation. The moment you let strangers message each other and meet in person, you have taken on responsibility for safety. That is why the admin panel exists and why I built it early rather than as an afterthought. Reporting, blocking, reviewing flagged conversations, and removing bad actors are first-class flows, not bolted-on patches.

I do not think you can build a companion app honestly without taking this seriously. The technology, the sockets and the sign-in and the maps, is the easy 60 percent. The remaining 40 percent is the human layer: making people feel safe enough to accept the invitation in the first place. Eşlik Et taught me that a social product is really a trust product wearing an app's clothing, and every technical decision either earns that trust or quietly erodes it.