AI-powered food logging for Google Health. Take a photo of your meal, let Claude analyze the nutrition, and log it directly to Google Health.
What It Does
- Photo capture — Take a photo of your food with your phone camera
- AI analysis — Claude analyzes the image and estimates nutritional information
- Review & edit — Confirm or adjust the nutrition data
- Log to Google Health — Post directly to your Google Health food log
Single-user application with email allowlist.
Tech Stack
- Next.js 16+ (App Router, TypeScript)
- Tailwind CSS + shadcn/ui for styling
- iron-session for encrypted cookie-based sessions
- Google OAuth 2.0 for authentication and Google Health API access
- Google Health REST API for food logging (single shared OAuth client)
- Anthropic Claude API for nutrition analysis (tool_use)
- PostgreSQL via Railway (Drizzle ORM)
- Railway for deployment
Environments
| Environment | Branch | URL | Health API |
|---|---|---|---|
| Production | release |
food.lucaswall.me |
Live |
| Staging | main |
food-test.lucaswall.me |
Dry-run (HEALTH_DRY_RUN=true) |
Branch strategy:
main— development branch, auto-deploys to stagingrelease— stable branch, auto-deploys to production- Feature branches → PR to
main→ merge to staging → mergemaintorelease
Each environment has its own Railway Postgres, environment variables, and domain.
Promotion flow: Merge main → release to deploy to production.
Deployment (Railway)
Prerequisites
- Railway account
- Railway CLI installed and authenticated (
railway login) - GitHub repository pushed to origin
Step 1: Create Railway Project
- Go to railway.com/new
- Choose "Deploy from GitHub Repo"
- Select the
food-scannerrepository - Railway creates the project and auto-deploys
Do not use railway init — that's for manual deploys, not GitHub-linked projects.
Step 2: Link CLI to Project
From the project directory:
railway link
Select the food-scanner project and environment when prompted. This stores the link in ~/.railway/config.json (global, not in the repo).
Step 3: Obtain OAuth Credentials
Follow the OAuth Setup section below to create Google OAuth credentials before setting environment variables.
Step 4: Set Environment Variables
railway variable set \
SESSION_SECRET="$(openssl rand -base64 32)" \
HEALTH_TOKEN_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
ALLOWED_EMAILS=wall.lucas@gmail.com \
APP_URL=https://food.lucaswall.me \
GOOGLE_CLIENT_ID=your-google-client-id \
GOOGLE_CLIENT_SECRET=your-google-client-secret \
ANTHROPIC_API_KEY=your-anthropic-api-key \
HEALTH_DRY_RUN=false \
LOG_LEVEL=info
HEALTH_DRY_RUN must be set explicitly (true or false) on every non-local host — the app refuses to boot otherwise. Production uses false.
For staging, override it:
railway variable set HEALTH_DRY_RUN=true
Note:
HEALTH_TOKEN_ENCRYPTION_KEYis a dedicated 32-byte random key for encrypting Google Health tokens at rest. Rotating this key invalidates all stored tokens — users must re-link Google Health after rotation.
Set real values for all credentials.
Do not set NODE_ENV — Railway handles this automatically. Setting NODE_ENV=development breaks the Next.js production build.
Step 5: Generate Public Domain
railway domain
This creates a public URL like https://food-scanner-production-XXXX.up.railway.app.
Step 6: Update OAuth Redirect URIs
Once you have your Railway domain, go back to the Google Cloud Console and add the production redirect URI:
- Google:
https://<your-railway-domain>/api/auth/google/callback
Step 7: Verify
curl https://food-scanner-production-XXXX.up.railway.app/api/health
Should return { "success": true, "data": { "status": "ok" }, "timestamp": ... }.
Step 8: Add PostgreSQL Database
- Open your project in the Railway dashboard
- Click "+ New" on the Project Canvas
- Select "Database" → "PostgreSQL"
- Railway creates a Postgres service with auto-generated credentials
Connect the database to your app service
- Click on the food-scanner service in the canvas
- Go to the Variables tab
- Click "+ New Variable"
- Add
DATABASE_URLwith value${{Postgres.DATABASE_URL}}— This uses Railway's reference variable syntax to dynamically resolve the Postgres connection string - Railway will redeploy automatically after saving
Verify the connection
After the redeploy completes:
railway logs
Look for successful startup with no database connection errors. The app runs migrations automatically on startup — no manual migration step is needed.
Build & Start
Railway builds with Railpack, which auto-detects Next.js:
- Build:
npm run build(runsnext build) - Start:
npm start(runsnext start) - Health check:
GET /api/healthreturns 200
railpack.json replaces Railpack's deprecated NPM_CONFIG_PRODUCTION=false with NPM_CONFIG_INCLUDE=dev, so the build still installs devDependencies without npm's production config warning on every install, build and start.
Watch Paths are set in the Railway service settings (Settings → Build), not in a repo file — Railway's railway.json Config as Code is deprecated. Keep them in sync with the app paths in .github/workflows/ci.yml, plus railpack.json and .node-version.
Monitoring
railway logs # Stream deploy logs
railway logs --build # Stream build logs
Or use the Railway MCP from Claude Code to query logs and deployment status.
Custom Domain (Optional)
- Add custom domain in Railway dashboard → Settings → Networking
- Configure DNS (CNAME record pointing to Railway)
- Update OAuth redirect URIs in the Google Cloud Console to use the custom domain
External Services Setup
Anthropic API
Claude powers the AI food analysis. The model id lives in src/lib/claude.ts.
- Go to console.anthropic.com
- Create an account or sign in
- Navigate to API Keys in the sidebar
- Click Create Key and copy the API key
- Add to Railway:
ANTHROPIC_API_KEY=sk-ant-api03-...
Note: The API key is included in Step 4 (Set Environment Variables) above.
OAuth Setup
Google OAuth
Food Scanner uses a single shared Google OAuth client for both login and Google Health API access.
- Go to Google Cloud Console
- Create a new project (or select existing)
- Navigate to APIs & Services → Credentials
- Click Create Credentials → OAuth 2.0 Client ID
- Select application type: Web application
- Under Authorized redirect URIs, add your environment URLs:
- Production:
https://food.lucaswall.me/api/auth/google/callback - Staging:
https://<staging-railway-domain>/api/auth/google/callback - Local:
http://localhost:3000/api/auth/google/callback
- Production:
- Copy the Client ID and Client Secret
Google Health Scopes
Configured on the GCP OAuth consent screen, not via environment variables. The four scopes requested (see GOOGLE_HEALTH_SCOPES in src/lib/auth.ts) are all under https://www.googleapis.com/auth/:
googlehealth.nutrition.writeonlygooglehealth.profile.readonlygooglehealth.health_metrics_and_measurements.readonlygooglehealth.activity_and_fitness.readonly
IMPORTANT: every googlehealth.* scope is restricted. That requires OAuth verification plus an annual CASA assessment, and the consent screen must be In production — while it is in Testing, Google revokes refresh tokens every 7 days and caps the app at 100 users.
Users connect via the one-click /app/connect-health flow after logging in.
Local Development
See DEVELOPMENT.md for complete local setup instructions.
PWA (Add to Home Screen)
The app supports "Add to Home Screen" on mobile devices for a native-like experience.
Features:
- Standalone display mode (no browser chrome)
- Custom app icon on home screen
- Portrait orientation lock
To install:
- Open the app in Safari (iOS) or Chrome (Android)
- Tap the Share button → "Add to Home Screen"
- The app launches in standalone mode
Customizing icons:
Replace the placeholder icons in public/:
icon-192.png— 192x192 PNGicon-512.png— 512x512 PNG
No service worker or offline support — the app requires an internet connection.
Documentation
| File | Description |
|---|---|
| API.md | External API reference (auth, endpoints, schemas) |
| ROADMAP.md | Feature ideas and specifications |
| CLAUDE.md | Technical reference for Claude Code |
| DEVELOPMENT.md | Local development setup |
| MIGRATIONS.md | Pending production data migrations |
| PLANS.md | Current implementation plan (managed by skills) |
| CHANGELOG.md | Version history |
| README.md | This file — deployment and operations |
