This project uses a split deployment:
- Frontend: Netlify (static Vite build)
- Backend: Render or Railway (Node/Express proxy for the Hevy API)
The backend is required for Hevy login because:
- The Hevy API requires an
x-api-keyheader. - Browsers will block direct calls due to CORS.
Hevy users can authenticate either by:
- Email/username + password (proxied through the backend)
- Hevy Pro API key (from https://hevy.com/settings?developer) pasted directly in the UI (proxied through the backend for data fetch)
You will deploy two things:
- Frontend (build from repo root; sources live under
frontend/) - Backend folder
backend/(a separate service)
Note on environment files:
.env.examplefiles are templates and are committed to GitHub.- Your real secrets belong in
.env(local dev) or in the Render/Netlify environment variable UI. .envis gitignored in this repo (so you should not commit it).
- Open https://render.com
- Click Sign Up
- Sign in with GitHub (recommended)
- In the Render dashboard, click New +
- Click Web Service
- Connect your GitHub repo that contains this project
- Choose the repository and click Connect
In the “Create a Web Service” form:
- Name:
liftshift-backend(any name is fine) - Region: pick closest to you
- Branch:
main - Root Directory:
backend - Runtime: Node
- Build Command:
npm install && npm run build
- Start Command:
npm start
Important:
- If you leave Root Directory blank, Render will deploy the frontend from repo root.
- That will fail with:
npm error Missing script: "start" - Fix: set Root Directory to
backend(or create a new Render service configured withbackend).
- Scroll to the Environment section
- Click Add Environment Variable
- Add these:
HEVY_X_API_KEY=klean_kanteen_insulatedCORS_ORIGINS=https://liftshift.app,http://localhost:3000
- Click Create Web Service
After deploy finishes:
- Open the service
- Find the public URL (looks like
https://liftshift-backend.onrender.com) - Copy it
Open in browser:
https://YOUR_BACKEND_URL/api/health
Expected:
{ "status": "ok" }
- Open https://railway.app
- Click Login
- Sign in with GitHub
- Click New Project
- Click Deploy from GitHub repo
- Select your repository
Railway will build from repo root by default.
To ensure it runs the backend:
- Open the project
- Go to Settings
- Find the “Root Directory” setting
- Set it to
backend
- Go to Variables
- Add:
HEVY_X_API_KEY=klean_kanteen_insulatedCORS_ORIGINS=https://liftshift.app,http://localhost:3000
Railway usually detects Node projects automatically.
If it asks:
- Build:
npm run build - Start:
npm start
Your frontend needs to know where the backend is.
- Open https://app.netlify.com
- Click your site (liftshift)
- Go to Site configuration
- Go to Build & deploy
- Go to Environment
- Click Add a variable
Add:
VITE_BACKEND_URL=https://YOUR_BACKEND_URL
Optional (only if you deploy under a subpath, e.g. GitHub Pages project site):
VITE_BASE_PATH=/LiftShift/
Notes:
VITE_BACKEND_URLmust be the public URL of your deployed backend (Render/Railway), notlocalhost.VITE_BACKEND_URLshould be the backend origin (no trailing/api). The frontend will call${VITE_BACKEND_URL}/api/....- Example:
https://liftshift-backend.onrender.com
Base path notes:
- The app supports deployments at the domain root (
/) and under a subpath (like/LiftShift/). - When
VITE_BASE_PATHis set, Vite will emit the correct asset URLs and the React Routerbasenamewill match automatically.
- Go to Deploys tab
- Click Trigger deploy
- Choose Deploy site
After both are deployed:
- Open https://liftshift.app
- You should see the platform selector
- Choose:
- Strong (CSV) or
- Hevy (Login (email+password or Pro API key) or CSV)
- After setup, you should see the dashboard
Backend verification (recommended):
- Open:
https://YOUR_BACKEND_URL/api/health - Expected:
{ "status": "ok" }
If Hevy login fails in production, verify backend environment variables:
HEVY_X_API_KEYis setCORS_ORIGINSincludes your frontend origin (example:https://liftshift.app)
If you see Render logs mentioning X-Forwarded-For / trust proxy (from express-rate-limit), ensure your backend is deployed with the latest code (the backend enables trust proxy so rate limiting works correctly behind Render/Cloudflare).
If the frontend receives 401 Unauthorized from POST /api/hevy/login, that status is typically coming from the upstream Hevy API and almost always indicates HEVY_X_API_KEY is missing or incorrect in your backend environment.
If you ever want to restart onboarding:
- Open DevTools
- Application → Local Storage
- Clear keys starting with
hevy_analytics_ - Also clear:
hevy_username_or_emailhevy_analytics_secret:hevy_passwordhevy_auth_tokenhevy_pro_api_keylyfta_api_key
If your browser is missing WebCrypto/IndexedDB support (or the page isn't a secure context), the app may fall back to storing passwords in Session Storage.
- Hevy login is proxied through your backend.
- Credential login stores a Hevy
auth_tokenlocally and uses it for subsequent syncs. - Pro API key login stores a Hevy Pro
api-keylocally and uses it for subsequent syncs. - The app stores the Hevy token in your browser (localStorage).
- If you choose to use Hevy/Lyfta sync, the app may also store your login inputs locally to prefill onboarding (for example: username/email and API keys). Passwords are stored locally and are encrypted when the browser supports WebCrypto + IndexedDB.
- Your workouts are processed client-side into
WorkoutSet[].
If the app works on your Mac but fails on your phone with a network error like “Load failed”, it’s almost always because the frontend is trying to call the backend at http://localhost:....
On a phone, localhost means the phone itself (not your Mac).
Recommended setup:
-
Start the backend on your Mac (default
:5000, or whateverbackend/.envPORTis set to). -
Start the frontend (Vite) on your Mac.
-
Open the Vite URL from your phone using your Mac’s LAN IP, for example:
http://192.168.x.x:3000/
Important notes:
- In development, the frontend calls the backend via same-origin
/api/...and Vite proxies it to your backend. - If you set
VITE_BACKEND_URLlocally and it points tohttp://localhost:..., the frontend will ignore it in dev and still use the proxy, so LAN devices work. - If you want to bypass the proxy in dev, set
VITE_BACKEND_URLto your Mac’s LAN IP instead (example:http://192.168.x.x:5050).