Inspiration

Two years ago, my wife and I built CafeDiary for Japan — a small app for remembering the cafés you visit. It found real users, and some of them became paying subscribers. It's a genuinely lived-in app: my wife designs and tests, I code, and our weekends are field research.

When we looked at the global café-app landscape for Shipaton, we saw two extremes: competitive ranking apps that turn every latte into a leaderboard, and utility finders that treat cafés as wifi-and-outlet vending machines. Nothing in between felt like ours — a quiet diary where your visits accumulate into something beautiful, and where friends show up as company, not competition.

So we built a new app for the rest of the world.

What it does

A café diary on a map. Every café visit you log — star rating, photos, tags, a note — becomes a pin on your personal map. Over time, your map fills up with your story.

  • Map = home. One map, three layers: Mine, Friends, Wishlist. Follow a friend and their favorite cafés appear as avatar pins; when several friends love the same place, their avatars stack ("+2").
  • Café pages. Tap any café to see community ratings, top tags (wifi, cozy, specialty coffee...), your friends' logs, and public logs from other users — with the Google Maps rating alongside for reference. That's also how you discover people worth following.
  • Friends' maps. Open "Ana's cafés" — from her profile, or straight from the Friends row on any café sheet — and plan your coffee crawl around a real person's taste, not an algorithm's.
  • You decide who sees each visit. The first time you log a visit, CafeDiary asks whether your visits should appear on café pages or stay just for you — and remembers your answer. You can flip it per visit, anytime. No rankings. No leaderboards. No streaks.
  • Share the moment, not the score. Any visit becomes a share-ready card in one tap — your photo, your stars, your note, in the app's stationery style. At the end of each month, a Monthly Recap card sums up your café month (visits, new finds, top tag, average rating). Both are drawn entirely from your own data: no map tiles, no Google assets, no extra API cost — just something worth posting.
  • Premium (via RevenueCat): the free plan includes 5 photo uploads per month; Premium makes photos unlimited — $2.99/month or $19.99/year, with a 7-day free trial on the annual plan. Details in "Monetization" below.

How we built it

  • Expo SDK 54 / React Native 0.81 / TypeScript / Expo Router, with ~350 automated tests on Node's built-in test runner guarding the rules that matter (source partitioning, marker keys, API-call discipline).
  • Supabase — and here's the unusual part: the global app shares one production database with the live Japanese app. Every row is tagged with source_app, migrations are strictly additive, and every schema change is rehearsed with RLS role impersonation before it touches production, because real Japanese users are on the other side of that database.
  • Google Places through our own Edge Function. Clients never call Google's metered Nearby APIs directly. The map asks our nearby-cafes function for a geohash tile; the function serves a 30-day cache and only calls Google on a true cache miss. Cost scales with unique areas explored per month, not with users × app opens. A thousand users in the same city cost us about one API call a month.
  • Forked from our production Japanese codebase — then we deleted ~6,800 lines (a coffee encyclopedia, ads, push notifications) to make it lighter, not heavier, and rebuilt the home screen around the map.
  • RevenueCat for the Premium subscription: entitlement-based gating, Offerings, restore, customer identity bound to the Supabase user ID.
  • react-native-maps with avatar-photo pins, a cold-start pin-drop animation built on Reanimated, and marker keys that never encode UI state (more on that below).
  • react-native-view-shot renders the share card and Monthly Recap as PNGs from React Native views — same fonts, same paper, no server.

Challenges we ran into

The billing incident. In the Japanese app's early days, our timeline fetched café photos from Google Places on every render. The invoice that month taught us a lesson no documentation could: metered APIs must be constrained by architecture, not by discipline. For the global app we designed the tile cache first, before writing any map code. Photos are fetched exactly once, when a user opens a café's detail sheet — never in lists, never on pan, never on launch.

The native crash you can't try/catch. Build 23 of the Japanese app crashed on iOS whenever users toggled map filters. The cause: we had encoded filter state into marker keys, forcing react-native-maps to tear down and recreate native views mid-gesture. The fix — keys are café IDs, period; animation and filter state live inside the marker's child views — is now a hard rule written into this repo's agent instructions, so even our AI coding assistants can't reintroduce it.

Sharing a production database with live users. Additive-only migrations, language partitioning (Japanese user content never renders in the global UI — only anonymous, language-neutral star and tag aggregates are shared), and a fail-soft client that works even before the backend pieces are deployed.

Accomplishments that we're proud of

  • First commit on August 27; store-ready in about four weeks, as a two-person family team with day jobs.
  • A monetization design where free users cost us almost nothing to serve (the tile cache makes marginal API cost ~zero), so the freemium math actually works at small scale.
  • Released on the App Store worldwide (Japan excluded, where the original app serves users) in September 2026.
  • ~350 automated tests green at submission.
  • The cold-start pin-drop: your pins raining onto the map in under a second, from our own data, costing zero API calls.

What we learned

  • Constraints make better products. Our map allows exactly five permanent UI elements — every feature idea had to fight for a spot or find another home (most found a better one).
  • Monetize the emotional core, not the utility. People will pay to keep memories (photos), not to unlock a search filter.
  • Ship the boring safety rails first: report, block, terms agreement, restore purchases. They're invisible until they're the only thing that matters.
  • A production incident is the best design document you'll ever get — if you let it be.

What's next

  • v1.1: friends-only visibility, opening hours on café sheets, a map view for lists, an abstract mini-map on the share card (your pins, our drawing — still no tiles), CSV export, and a home-screen widget.
  • Premium v2: a "Year in cafés" recap and yearly stats, with the annual trial as the default entry point.
  • Localization: the codebase is English-first now, with i18n extraction planned once the store listing proves demand outside English markets.
  • Cross-pollination: the Japanese app keeps feeding anonymous ratings and tags into the global café pages — two apps, one growing dataset.

Monetization

Pricing. CafeDiary Premium is $2.99/month or $19.99/year (about 44% off), with a 7-day free trial on the annual plan. The free plan has everything else — the map, unlimited visits, friends, lists, share cards, the Monthly Recap — and 5 photo uploads per month. Premium removes the photo cap.

Why photos. Photos are the emotional core of a diary — the thing people scroll back to — and they are also our only real variable cost (storage and egress). The paywall sits exactly where our cost curve bends. We don't gate search filters, friends, or the map: those cost us nothing per user, and gating them would shrink the network that makes the app worth using.

Why the math works. Most café apps pay Google per user × per open. We pay per unique area per month: the map asks our Edge Function for a geohash tile, the function serves a 30-day cache, and Google is called only on a true miss. Free users therefore cost ≈ $0 to serve, and every Premium dollar is margin rather than subsidy. We can keep the free tier generous forever without it eating the business.

Validated, not hypothetical. Our Japanese app has run the same photo-cap Premium at the same price points since launch. Roughly 1 in 20 of its active users subscribes. Small sample, real money — enough to know the model works before we translated it.

How RevenueCat supports it.

  • Entitlement premium is the single source of truth for the photo cap.
  • Offerings let us change packages and prices without shipping a build.
  • logIn binds the RevenueCat customer to the Supabase user ID, so Premium follows the account across devices and reinstalls.
  • "Restore purchases" lives on the paywall; the whole flow was tested end-to-end in Sandbox.
  • For judges, the demo account has Premium via a Granted Entitlement in the RevenueCat dashboard — no purchase needed to see the full app.

Early results. Numbers as of the submission date will be added here.

Design — where to look

  1. The pin drop. On a cold start, your visited cafés fall onto the map nearest-first, in under a second. It's the app's thesis in one motion: this map is yours, built from places you actually went.
  2. Avatar pins and the "+2" stack. Friends appear as their photo, not a generic marker. When several friends love the same café, the avatars stack with a count — company, not competition.
  3. The stationery form. Logging a visit feels like writing in a notebook: cream paper, a handwriting typeface (Klee One), tag chips that read like stickers. Nothing about it says "submit a review."
  4. Share card and Monthly Recap. The same stationery, rendered as a Stories-sized card from your own data. No map tiles, no Google assets — so it can live anywhere without attribution baggage.
  5. The five-element rule. The map's permanent UI is exactly five things: search, notifications, the three layer chips, my-location, and the "+" button. Everything else lives in a sheet or behind a person.
  6. The cream map. A custom, desaturated map style so the pins carry the color and the map reads as paper rather than navigation software.

Built With

Share this project:

Updates

Submission history