Local Development
This guide covers everything you need to run Soku locally, from initial setup to debugging production-like issues on your machine.
Prerequisites
Before starting, make sure you have:
- Node.js 22 (see
enginesin rootpackage.json; usenvm useif you have an.nvmrc) - pnpm 9.x installed globally (
npm install -g pnpm) - A populated
.env.localfile inapps/web/(see Environment Setup below) - Firebase project credentials for Auth and Firestore
- Stripe test-mode API keys (for subscription flows)
Starting the Dev Server
Full Stack (Recommended)
The root pnpm dev command uses Turborepo to start all services in parallel:
pnpm dev
This runs:
- The Next.js web app (
apps/web) - The Firebase Functions emulator (
apps/functions)
Both processes stream logs to the same terminal. Turbo handles orchestration so you do not need multiple terminal tabs.
Individual Services
If you only need part of the stack:
| Command | What it starts |
|---|---|
pnpm dev:web | Next.js web app only |
pnpm dev:functions | Firebase Functions emulator only |
Ports and URLs
| Service | URL | Default Port |
|---|---|---|
| Next.js web app | http://localhost:3000 | 3000 |
| Firebase Functions emulator | http://localhost:5001 | 5001 |
| Firestore emulator | http://localhost:8080 | 8080 |
| Storage emulator | http://localhost:9199 | 9199 |
Environment Setup
The web app requires several environment variables. Create apps/web/.env.local with:
Required Variables
# Firebase (client-side)
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
# Firebase (server-side / Admin SDK)
FIREBASE_SERVICE_ACCOUNT_KEY= # JSON string or path
# Stripe
STRIPE_SECRET_KEY= # sk_test_...
STRIPE_WEBHOOK_SECRET= # whsec_...
STRIPE_STARTER_PRICE_ID=
STRIPE_PRO_PRICE_ID=
STRIPE_AGENCY_PRICE_ID=
STRIPE_STARTER_YEARLY_PRICE_ID=
STRIPE_PRO_YEARLY_PRICE_ID=
STRIPE_AGENCY_YEARLY_PRICE_ID=
Optional Variables
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
TIKTOK_CALLBACK_URL= # Production callback URL for TikTok OAuth
Verifying Environment Variables
Hit the debug endpoint to check which variables are present without revealing their values:
GET http://localhost:3000/api/debug-env
This returns a list of variable names with a present: true/false flag. It never exposes actual secret values.
Hot Reload Behavior
Next.js 16 provides Fast Refresh out of the box:
- React components: Edits to
.tsxfiles hot-reload in the browser with state preserved. - Server components / API routes: Changes trigger a server restart. The browser refetches automatically.
- Tailwind CSS: Class changes appear instantly via Fast Refresh.
- Environment variables: Changes to
.env.localrequire a full restart (Ctrl+Cthenpnpm dev).
Firebase Functions running through the emulator also watch for file changes and restart automatically.
Error Handling in Development
When NODE_ENV=development (the default for next dev), API routes return detailed error responses including stack traces. This makes debugging straightforward:
{
"error": "Error creating checkout session",
"details": "Missing required parameters: planId",
"stack": "Error: Missing required parameters...\n at POST (/app/api/...)"
}
In production, error responses omit stack traces and internal details.
Platform-Specific Dev Tips
TikTok Desktop Auth Flow
TikTok's OAuth has a special desktop flow that behaves differently from mobile:
- Append
?desktop=1to the TikTok auth start URL when testing locally. - The callback is handled by a proxy route at
/api/tiktok/callbackbecause TikTok's desktop app has restrictions on redirect URIs. - The callback handler automatically serves TikTok's verification
.txtfiles when probed. - Make sure
TIKTOK_CALLBACK_URLmatches your deployed URL for production. For local development, the proxy route handles the flow.
Social Integrations
Use the Settings page in the app to connect social platform integrations. The app uses Firebase Functions start endpoints and redirects back to your current URL when the OAuth flow completes.
Common Commands
| Command | Description |
|---|---|
pnpm dev | Start all services in parallel via Turbo |
pnpm dev:web | Start only the Next.js web app |
pnpm dev:functions | Start only the Firebase Functions emulator |
pnpm build | Production build (all packages) |
pnpm test | Run unit tests with coverage |
pnpm lint | Run ESLint across the monorepo |
pnpm typecheck | Run TypeScript type checking |
pnpm check | Lint + typecheck + test (pre-push quality gate) |
pnpm ci | Full CI pipeline: lint, typecheck, test, build |
pnpm deploy-functions | Deploy all Firebase Functions |
pnpm deploy-function --function=<name> | Deploy a single Firebase Function |
pnpm sync:schema | Build and sync the shared schema package |
Debugging Tips
Inspecting API Routes
Use curl or a tool like Postman to test API routes directly:
# Check environment variables
curl http://localhost:3000/api/debug-env
# Test authenticated route (replace TOKEN with a Firebase ID token)
curl -X POST http://localhost:3000/api/auth/session \
-H "Content-Type: application/json" \
-d '{"idToken": "TOKEN"}'
Stripe Webhook Testing
To test Stripe webhooks locally, use the Stripe CLI to forward events to your Functions emulator:
stripe listen --forward-to http://localhost:5001/<project-id>/us-central1/stripeWebhook
The CLI prints a webhook signing secret (whsec_...) that you should set as STRIPE_WEBHOOK_SECRET in your Functions environment.
Firebase Emulator UI
When running the Firebase emulators, the Emulator UI is available at http://localhost:4000. It provides a visual interface for inspecting Firestore documents, Auth users, and Storage files.
Common Issues
| Problem | Solution |
|---|---|
Missing Firebase environment variables | Ensure all NEXT_PUBLIC_FIREBASE_* vars are set in .env.local |
| Session cookie not being set | Check that __session cookie is visible in DevTools; ensure the session API route is running |
| Stripe webhook errors | Verify STRIPE_WEBHOOK_SECRET matches the secret from stripe listen |
| Port already in use | Kill the existing process on port 3000 or 5001, then restart |
| HMR not working | Clear .next cache: rm -rf apps/web/.next and restart |