Overview
Recipe Room has three deployment targets, each serving a different purpose in the current phased rollout:
| App | Platform | Status | What It Deploys |
|---|
apps/mobile | EAS → App Store | Active — primary product | iOS app via TestFlight / App Store |
apps/web-interim | Vercel | Active — supports mobile | Auth callbacks, deep links, landing page |
apps/web | Vercel | Phase 2 — not deployed | Full web client (future) |
The mobile app is the production product. The web interim exists to handle things the mobile app can’t do on its own — email confirmation callbacks, password reset flows, universal link verification, and a public landing page with waitlist signup. The full web app will be deployed in Phase 2 when the web client is built out.
Mobile Deployment (EAS)
The iOS app is built and submitted via Expo Application Services (EAS). This is the primary deployment pipeline.
Build Profiles
Defined in apps/mobile/eas.json:
| Profile | Purpose | Command |
|---|
development | Dev client with debugging tools | pnpm eas:build:dev |
preview | TestFlight / internal testing | pnpm eas:build:preview |
production | App Store release | pnpm eas:build |
Submitting to App Store
Run commands from apps/mobile/:
pnpm eas:build:submit
pnpm eas:build:submit:no-fingerprint
pnpm eas:submit
Detailed mobile build rules are centralized in Mobile Build Workflow.
Mobile Environment Variables
Set in apps/mobile/.env locally, and in EAS secrets for CI builds:
| Variable | Required | Description |
|---|
EXPO_PUBLIC_SUPABASE_URL | Yes | Supabase project URL |
EXPO_PUBLIC_SUPABASE_ANON_KEY | Yes | Supabase anonymous key |
EXPO_CONFIG_MODE | No | Set automatically by the mobile scripts and EAS build profiles |
EXPO_CONFIG_MODE controls which app config is used — development for Expo Go compatibility, production for EAS builds with the production bundle identifier. See Mobile Build Workflow for the current setup.
Web Interim Deployment (Vercel)
apps/web-interim deploys to Vercel automatically on push to the main branch. It runs on a separate Vercel project from the future web app.
What It Serves
- Auth callback pages (email confirmation, password reset, email change)
- Deep link verification files (
.well-known/apple-app-site-association, .well-known/assetlinks.json)
- Content redirect pages (
/recipes/[id], /posts/[id], /collections/[id]) that either deep link into the app or show an “app only” fallback
- Public landing page with waitlist signup (via
marketing-events Supabase edge function)
Environment Variables
Set in Vercel project settings:
| Variable | Required | Description |
|---|
NEXT_PUBLIC_SUPABASE_URL | Yes | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Yes | Supabase anonymous key |
That’s it — web interim doesn’t need the service role key because it doesn’t do server-side data mutations. The /api/waitlist route invokes the marketing-events Supabase edge function, which handles the Loops API key server-side.
Build Command
pnpm build --filter @recipe-room/web-interim
Vercel should be configured to build from apps/web-interim/ with the root directory set accordingly.
Web App Deployment (Phase 2)
apps/web is scaffolded but not deployed yet. When Phase 2 begins, it will deploy to Vercel as a separate project.
How It Will Differ from Web Interim
The full web app will route all data mutations through server-side API routes (using the service role key), unlike the mobile app which talks to Supabase directly. This means:
- API routes handle auth, CRUD, and business logic server-side
- The service role key stays on the server (never exposed to the browser)
- Middleware handles route protection and auth redirects
next/image and Vercel’s image CDN handle image optimization
Future Environment Variables
| Variable | Required | Description |
|---|
NEXT_PUBLIC_SUPABASE_URL | Yes | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Yes | Supabase anonymous key (safe for client) |
SUPABASE_SERVICE_ROLE_KEY | Yes | Supabase service role key (server-only) |
SUPABASE_SERVICE_ROLE_KEY must never be prefixed with NEXT_PUBLIC_. It is only used in API routes (server-side).
Phase II web routes should reuse the same marketing-events edge function used by mobile and web interim. No app-level Loops API key is expected in the web app environment.
Middleware (Phase 2)
The Next.js middleware (apps/web/src/middleware.ts) will handle route protection:
Protected routes (redirect to /login if unauthenticated): /me, /settings, /recipes/new, /collections/new, /favorites, any route containing /edit.
Auth routes (redirect to / if already authenticated): /login, /register.
The middleware uses supabase.auth.getUser() to validate the JWT. When redirecting to login, the original path is preserved as a redirect query parameter so the user returns to their intended destination after logging in.
Loops.so Integration
Recipe Room uses Loops.so for transactional emails and audience management. The integration is optional — if the edge function secret is not set, all Loops operations are silently skipped.
What It Does
| Event | Action | Where |
|---|
| User registers | Creates contact in Loops, sends welcome email | Mobile: marketingGateway.registerUserMarketing() via marketing-events edge function |
| User deletes account | Removes contact from Loops | Mobile: marketingGateway.removeUserMarketing() via marketing-events edge function |
| Waitlist signup | Adds email to Loops audience | apps/web-interim /api/waitlist route via marketing-events edge function |
All Loops API calls are routed through the marketing-events Supabase edge function. The Loops API key is stored as an edge function secret — it never appears in client or server code. The future web app should reuse the same edge function pattern rather than introducing a separate Loops key.
Configuring Loops
- Create an account at loops.so
- Create a transactional email template with ID
welcome-email
- Add the
firstName data variable to the template
- Add your API key as a Supabase edge function secret (
LOOPS_API_KEY)
All Loops calls are wrapped in try/catch — failures are logged but never block the primary operation.