City App — Mobile and Admin Panel
Kütahya Sosyal
A city app that answers "what's on in town this week" from a single place: an Expo mobile client, a Next.js admin panel, and a self-hosted Supabase stack feeding both through one shared data-access layer.
- Role
- Solo Developer — mobile, panel, data layer, and server setup
- Period
- July 2026 — Ongoing
- Focus
- Running a mobile app and an admin panel off one data-access layer and one schema, even though their session models are opposites.
01 / Problem
What needed solving?
Nothing about the city sits in one place. An event is announced on a venue's Instagram, a concert poster lives in a WhatsApp group, the on-duty pharmacy is on the pharmacists' association site, a municipal event is a PDF on a corporate page. The bottleneck is not missing information but scattered, unverifiable information: people check five places, cannot tell whether any of them is current, and most sources were never meant to be read on a phone. The question degrades from "where should I go" to "who can I ask" — and a student new to the city has no one to ask.
02 / Approach
Building the product as a whole.
The mobile app and the panel read the same schema, so they live in one repository; in separate repositories the two sides' types drift silently every time the schema changes, and that drift surfaces at runtime rather than at build time. The data-access layer sits in a shared package and takes the Supabase client as a parameter: on mobile the session lives in secure storage on the device and on the client side, in the panel it is read from a cookie on the server and rebuilt on every request — one query body feeds both worlds. Supabase runs on its own server rather than as a managed service; there is no subscription cost and no media ceiling, and the price — patching, backups, and uptime — was accepted openly.
03 / Technical architecture
The system's layers
- 01
Mobile client
File-based routing with Expo Router across four tabs — home, map, social, and profile. Styling is NativeWind with token-driven light and dark themes. Server state lives in React Query and is persisted, so the app opens on its last known state with no connection.
- 02
Shared data layer
Generated database types, data-access modules, and query factories in one package. Every function takes the Supabase client as a parameter, so query logic, field selection, signed-media generation, and cache keys stay in one place — and a query runs with whoever's session is holding it.
- 03
Admin panel
Content modules, a moderation queue, users, a blocklist, and an audit log on the Next.js App Router. Writes are Server Actions that each pass schema validation and an authorization gate before writing to the audit log. Moderation screens use a client that bypasses row-level security, because the panel's whole job is seeing what is not published.
- 04
Self-hosted stack
Postgres, auth, REST, realtime, storage, and the gateway run together under Docker Compose; the panel ships from its own compose file, and the storage backend switches from filesystem to S3 with a single variable. Setup is ordered — reset the schema, apply migrations, load the seed — followed by dated, re-runnable SQL patches.
04 / Product scope
Key features
- 01
What's on this week — events by date, with location and attendance on the detail page
- 02
Places to visit — by category, with photographs and directions
- 03
City news and the day's on-duty pharmacies in one feed
- 04
Events and places on a single map, with clusters that open as you zoom
- 05
A social layer: photo and video posts, likes, comments, follows, and messaging
- 06
Content entry, report moderation, a blocklist, and an audit log from the panel
05 / Stack
The tools used for this work.
- Expo SDK 56
- React Native
- React 19
- TypeScript
- NativeWind
- React Query
- MapLibre
- Next.js 16
- Tailwind CSS
- zod
- Supabase
- PostgreSQL 17
- PostgREST
- GoTrue
- Turborepo
- pnpm
- Docker Compose
Interface
Product screens




Result
What changed?
The product is still in development and has not shipped: it is in no store, has no real users, and therefore has no usage figures. What works end to end today: the discovery core, the map and its clustering, the social feed, follows, messaging, in-app notifications, account deletion, and the panel's content and moderation modules. Push notification is ready on the database side but waiting on the device side, and the jobs integration has its code in place but not its configuration. Screenshots are from the development environment with demo data.
