OneApp Docs
Platform Apps

Platform Apps

Learn about and work with all 7 platform applications that make up the OneApp ecosystem — including the main consumer app, API server, admin panel, documentation site, component library, mobile app, and email templates.

Know which app you need?

Why platform apps matter

Managing multiple applications without proper organization creates problems:

  • Duplicate infrastructure — Every app rebuilds authentication, database access, UI components from scratch
  • Version drift — Apps use different versions of React, Next.js, causing deployment conflicts
  • No code sharing — Same features implemented differently across web, mobile, admin
  • Configuration chaos — Each app has different ESLint rules, TypeScript configs, env vars
  • Deployment confusion — No clear understanding of which app runs where on what port
  • Team friction — Frontend team doesn't know which app to work on for specific features

OneApp's platform architecture uses a unified monorepo with shared packages (@repo/*) — all apps share authentication (@repo/auth), database client (@repo/db-prisma), UI components (@repo/uni-ui), and AI integration (@repo/ai) — ensuring consistency across web, mobile, and admin experiences.

Production-ready with 7 battle-tested applications (handling production traffic daily), microfrontend architecture for code splitting, shared environment configuration, unified deployment pipeline, automatic dependency updates, and instant hot-reload during development.

Use cases

Master platform apps to:

  • Build features faster — Share authentication, database, UI across all apps
  • Develop locally — Run full stack with one command (pnpm dev:oneapp)
  • Scale independently — Deploy web app separately from API server
  • Maintain consistency — All apps use same design system, auth flow, error handling
  • Test components — Preview UI in Storybook before using in production
  • Ship to mobile — Use same components in React Native via @repo/uni-ui

Quick Start

Run the full stack

# Start main app with microfrontends (recommended)
pnpm dev:oneapp

# This starts:
# - oneapp-onstage (port 3500) - Main consumer app
# - oneapp-backstage (port 3600) - Admin panel (microfrontend)
# - oneapp-api (port 3001) - REST API server

Open in browser:

That's it! The full stack is running with live reload.

Run individual apps

# Documentation site
pnpm --filter=docs dev                # Port 3000

# Component showcase
pnpm --filter=storybook dev           # Port 6006

# Mobile app (Expo)
pnpm --filter=mobile-app dev          # Opens Expo DevTools

# Email templates
pnpm --filter=email dev               # Port 3004

App architecture overview

The platform follows a microfrontend architecture where the main app (onstage) hosts smaller apps (backstage) as embedded modules:

┌─────────────────────────────────────────────────────────────┐
│                  oneapp-onstage (3500)                      │
│                  Main Consumer Application                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │  Chat & AI   │  │  Documents   │  │ Presentations│      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│                                                              │
│         ┌────────────────────────────────────────┐          │
│         │   oneapp-backstage (Microfrontend)     │          │
│         │   /backstage route - Admin Panel       │          │
│         └────────────────────────────────────────┘          │
└─────────────────────────────────────────────────────────────┘


               ┌──────────────────────────────┐
               │    oneapp-api (3001)         │
               │    REST API Server           │
               │  GET/POST/PUT/DELETE /api/*  │
               └──────────────────────────────┘


               ┌──────────────────────────────┐
               │   Neon Postgres Database     │
               │   + Upstash Redis Cache      │
               │   + Upstash Vector (RAG)     │
               └──────────────────────────────┘

Data flow:

  1. User → Interacts with onstage UI
  2. onstage → Calls oneapp-api for data
  3. oneapp-api → Queries Postgres/Redis/Vector DB
  4. Response → Returns to onstage → Displays to user

Complete app reference

All 7 platform applications

Consumer Applications

AppPortFrameworkPurposeDocumentation
oneapp-onstage3500Next.js 16Main consumer application with chat, documents, presentationsView docs →
mobile-app-Expo 52 + Next.js 16iOS/Android app with offline-first architectureView docs →

Use when: Building user-facing features, chat interfaces, document editing

Backend & APIs

AppPortFrameworkPurposeDocumentation
oneapp-api3001Next.js 16Dynamic CRUD REST API with auto-generated endpointsView docs →

Use when: Building API endpoints, server-side logic, database operations

Admin & Configuration

AppPortFrameworkPurposeDocumentation
oneapp-backstage3600Next.js 16AI workflow designer, admin panel, user managementView docs →

Use when: Managing workflows, administering users, configuring AI agents

Developer Tools

AppPortFrameworkPurposeDocumentation
docs3000Docusaurus 3Project documentation with versioningView docs →
storybook6006Storybook 8Component library showcase and testingView docs →
email3004React EmailEmail template preview and developmentView docs →

Use when: Writing documentation, developing components, previewing emails

Shared dependencies

All platform apps share core packages for consistency:

PackageUsed ByPurpose
@repo/authonstage, backstage, mobileBetter Auth authentication
@repo/db-prismaonstage, api, backstagePrisma 7 database client
@repo/aionstage, backstage, mobileVercel AI SDK v6 wrapper
@repo/uionstage, backstageReact 19 web components
@repo/uni-uionstage, mobile, docsUniversal web + native components
@repo/emailonstage, api, emailReact Email templates
@repo/analyticsonstage, mobile, docsPostHog analytics
@repo/observabilityonstage, api, backstageSentry error tracking

Benefits:

  • ✅ Single version of React, Next.js, TypeScript across all apps
  • ✅ Update a component once, all apps get the change
  • ✅ Shared authentication state between web and mobile
  • ✅ Consistent error handling, logging, analytics

Development workflow

Full stack development

# Terminal 1: Start full stack
pnpm dev:oneapp

# Terminal 2: Watch for package changes
pnpm --filter=@repo/uni-ui dev

# Terminal 3: Run tests
pnpm --filter=oneapp-onstage test --watch

Component development

# Terminal 1: Storybook for component preview
pnpm --filter=storybook dev

# Terminal 2: Main app to test integration
pnpm --filter=oneapp-onstage dev

Mobile development

# Terminal 1: Mobile app
pnpm --filter=mobile-app dev

# Terminal 2: API server (for data)
pnpm --filter=oneapp-api dev

# Scan QR code with Expo Go app to test on device

Next steps

For Developers: Environment configuration, deployment, and troubleshooting

Environment configuration

Shared environment variables

All apps share common environment variables for consistency:

Database (All Apps)

.env.local
# Neon Postgres
DATABASE_URL="postgresql://user:password@host.neon.tech/db?sslmode=require"
DATABASE_URL_UNPOOLED="postgresql://user:password@host.neon.tech/db?sslmode=require"

# Upstash Redis
UPSTASH_REDIS_REST_URL="https://your-redis.upstash.io"
UPSTASH_REDIS_REST_TOKEN="your-token"

# Upstash Vector
UPSTASH_VECTOR_REST_URL="https://your-vector.upstash.io"
UPSTASH_VECTOR_REST_TOKEN="your-token"

Authentication (onstage, backstage, mobile)

.env.local
# Better Auth
BETTER_AUTH_SECRET="your-secret-key"
BETTER_AUTH_URL="http://localhost:3500"
TRUSTED_ORIGINS="http://localhost:3500,http://localhost:3600"

# OAuth Providers
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"

GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"

Important: Keep BETTER_AUTH_SECRET consistent across all apps for session sharing.

AI Services (onstage, backstage, mobile)

.env.local
# Cloudflare AI Gateway
AI_GATEWAY_API_KEY="your-gateway-key"
AI_GATEWAY_URL="https://gateway.ai.cloudflare.com/v1/account/gateway/ai"

# LLM Providers
OPENAI_API_KEY="sk-..."
ANTHROPIC_API_KEY="sk-ant-..."
GROQ_API_KEY="gsk_..."

Observability (All Production Apps)

.env.local
# Sentry
SENTRY_DSN="https://...@sentry.io/..."
SENTRY_AUTH_TOKEN="your-auth-token"

# BetterStack
BETTERSTACK_SOURCE_TOKEN="your-source-token"

# PostHog
NEXT_PUBLIC_POSTHOG_KEY="phc_..."
NEXT_PUBLIC_POSTHOG_HOST="https://app.posthog.com"

App-specific environment variables

oneapp-onstage

platform/apps/oneapp-onstage/.env.local
# App-specific
NEXT_PUBLIC_APP_URL="http://localhost:3500"
NEXT_PUBLIC_API_URL="http://localhost:3001"
NEXT_PUBLIC_BACKSTAGE_URL="http://localhost:3600"

# Feature flags
NEXT_PUBLIC_ENABLE_AI_CHAT="true"
NEXT_PUBLIC_ENABLE_DOCUMENTS="true"
NEXT_PUBLIC_ENABLE_PRESENTATIONS="true"

oneapp-api

platform/apps/oneapp-api/.env.local
# API Configuration
API_PORT="3001"
API_RATE_LIMIT="100"
API_TIMEOUT="30000"

# CORS
ALLOWED_ORIGINS="http://localhost:3500,http://localhost:3600"

mobile-app

platform/apps/mobile-app/.env.local
# Expo
EXPO_PUBLIC_API_URL="http://localhost:3001"
EXPO_PUBLIC_AUTH_URL="http://localhost:3500"

# EAS Build
EAS_PROJECT_ID="your-project-id"

Deployment guide

Vercel (onstage, api, backstage)

vercel.json
{
  "buildCommand": "pnpm build",
  "devCommand": "pnpm dev",
  "installCommand": "pnpm install",
  "framework": "nextjs",
  "outputDirectory": ".next"
}

Deploy:

# Install Vercel CLI
pnpm install -g vercel

# Link project
vercel link

# Deploy to preview
vercel

# Deploy to production
vercel --prod

Environment variables:

  • Set in Vercel Dashboard → Settings → Environment Variables
  • Separate values for Development, Preview, Production

Cloudflare Pages (docs)

wrangler.toml
name = "forge-docs"
compatibility_date = "2025-01-01"

[build]
command = "pnpm build"
output_directory = "build"

[[pages_plugins]]
binding = "ASSETS"

Deploy:

# Install Wrangler CLI
pnpm install -g wrangler

# Login
wrangler login

# Deploy
wrangler pages deploy build

Chromatic (Storybook)

# Install Chromatic CLI
pnpm add -D chromatic

# Deploy to Chromatic
pnpm --filter=storybook chromatic --project-token=<token>

Benefits:

  • ✅ Visual regression testing
  • ✅ Component change detection
  • ✅ Automated screenshot comparisons

EAS Build (mobile-app)

app.config.ts
export default {
  expo: {
    name: "OneApp Mobile",
    slug: "forge-mobile",
    version: "1.0.0",
    ios: {
      bundleIdentifier: "com.forge.mobile"
    },
    android: {
      package: "com.forge.mobile"
    }
  }
};

Build and submit:

# Install EAS CLI
pnpm install -g eas-cli

# Login
eas login

# Build iOS
eas build --platform ios

# Build Android
eas build --platform android

# Submit to App Store
eas submit --platform ios

# Submit to Play Store
eas submit --platform android

Port reference

Quick reference for all app ports:

PortAppURL
3000docshttp://localhost:3000
3001oneapp-apihttp://localhost:3001
3004emailhttp://localhost:3004
3500oneapp-onstagehttp://localhost:3500
3600oneapp-backstagehttp://localhost:3600
6006storybookhttp://localhost:6006
8081mobile-app (Expo)exp://localhost:8081

Port conflicts:

# Kill process on port 3500
lsof -ti:3500 | xargs kill -9

# Or use different port
pnpm --filter=oneapp-onstage dev -- -p 3501

Troubleshooting

App won't start

# Clean install
pnpm clean && pnpm install

# Rebuild dependencies
pnpm build

# Check port availability
lsof -ti:3500

Environment variables not loading

# Verify .env.local exists
ls -la platform/apps/oneapp-onstage/.env.local

# Check env validation
pnpm --filter=oneapp-onstage typecheck

# Restart dev server
# Cmd/Ctrl+C → pnpm dev

Microfrontend not loading

# Verify backstage is running
curl http://localhost:3600

# Check CORS configuration
# oneapp-api/.env.local:
# ALLOWED_ORIGINS="http://localhost:3500,http://localhost:3600"

# Restart both apps
pnpm dev:oneapp

Database connection fails

# Test connection
pnpm --filter=oneapp-api prisma db push

# Verify environment variables
echo $DATABASE_URL

# Check Neon dashboard
# https://console.neon.tech/

Mobile app not connecting to API

# Use local IP instead of localhost
# mobile-app/.env.local:
# EXPO_PUBLIC_API_URL="http://192.168.1.100:3001"

# Find your local IP
ipconfig getifaddr en0  # macOS
ip addr show            # Linux
ipconfig               # Windows

# Allow API CORS for mobile
# oneapp-api/.env.local:
# ALLOWED_ORIGINS="http://192.168.1.100:3500"

Build fails in CI

# Ensure frozen lockfile
pnpm install --frozen-lockfile

# Build in correct order
pnpm --filter=@repo/types build
pnpm --filter=@repo/utils build
pnpm --filter=oneapp-onstage build

# Or use dependencies
pnpm --filter=oneapp-onstage... build

Advanced patterns

Microfrontend architecture

Host app (oneapp-onstage):

next.config.ts
export default {
  async rewrites() {
    return [
      {
        source: "/backstage/:path*",
        destination: process.env.BACKSTAGE_URL + "/:path*"
      }
    ];
  }
};

Remote app (oneapp-backstage):

next.config.ts
export default {
  basePath: "/backstage",
  assetPrefix: process.env.NODE_ENV === "production" ? "https://backstage.example.com" : ""
};

Shared authentication

All apps use the same auth instance:

packages/auth/src/index.ts
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { prisma } from "@repo/db-prisma";

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql"
  }),
  emailAndPassword: {
    enabled: true
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!
    }
  }
});

Usage in apps:

oneapp-onstage/app/page.tsx
import { auth } from "@repo/auth/server";
import { headers } from "next/headers";

export default async function Page() {
  const session = await auth.api.getSession({
    headers: await headers()
  });

  return <h1>Welcome, {session?.user.name}</h1>;
}

Shared database client

All apps use the same Prisma instance:

packages/db-prisma/src/index.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
};

export const prisma = globalForPrisma.prisma ?? new PrismaClient();

if (process.env.NODE_ENV !== "production") {
  globalForPrisma.prisma = prisma;
}

Usage:

oneapp-api/app/api/users/route.ts
import { prisma } from "@repo/db-prisma";

export async function GET() {
  const users = await prisma.user.findMany();
  return Response.json(users);
}

CI/CD Pipeline

GitHub Actions workflow

.github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v2
        with:
          version: 10

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "pnpm"

      - run: pnpm install --frozen-lockfile

      - run: pnpm build

      - run: pnpm lint

      - run: pnpm typecheck

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Vercel
        run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }}

Performance optimization

Build caching with Turborepo

turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"],
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Selective builds

# Build only changed apps
pnpm --filter="[origin/main]" build

# Build app and dependencies
pnpm --filter=oneapp-onstage... build

# Build app and dependents
pnpm --filter=...@repo/uni-ui build

On this page