Skip to content

refactor: split web into separate web/api projects with shared backend package #1125

@e7h4n

Description

@e7h4n

Background

Currently, turbo/apps/web contains both frontend UI and backend API code in a single package. This means any change to the web app - including UI-only changes like landing page copy or styling - triggers the full cli-e2e test suite (~20 minutes).

Root cause: The CI uses turbo-ignore for change detection, which treats the entire web package as one unit.

Impact: Content/style PRs that don't affect CLI or runner functionality still wait 20+ minutes for cli-e2e to complete.

Proposed Solution: Full Package Extraction (Approach A)

Split into three packages:

turbo/
├── apps/
│   ├── api/           # NEW: API routes only (/api/*, /v1/*)
│   └── web/           # Frontend pages only
└── packages/
    └── backend/       # NEW: Shared backend code
        ├── src/
        │   ├── db/    # Schema (15 tables) + migrations (49 files)
        │   ├── lib/   # Business logic services (~68 files)
        │   ├── env.ts
        │   └── types/
        └── package.json

Expected CI Behavior After Split

Change Type cli-e2e Triggered?
web/ (UI pages, components, i18n) No
api/ (API routes) Yes
packages/backend/ Yes
packages/core/ Yes

Implementation Phases

Phase 1: Create @vm0/backend Package

  • Extract /src/db/ (schema, migrations)
  • Extract /src/lib/ (all services)
  • Extract /src/env.ts and /src/types/
  • Update web to import from @vm0/backend
  • Verify existing tests pass

Phase 2: Create turbo/apps/api

  • Create new Next.js app for API
  • Move /app/api/* routes (34 files)
  • Move /app/v1/* routes (21 files)
  • Configure to use @vm0/backend
  • Set up Vercel project for api.vm0.dev

Phase 3: Clean Up Web App

  • Remove API-only dependencies from web
  • Update CI change detection for api-changed
  • Update cli-e2e to depend on deploy-api instead of deploy-web
  • Clean up unused code

Technical Considerations

Files to Move

To packages/backend/:

  • src/db/schema/*.ts (15 tables)
  • src/db/migrations/ (49 files)
  • src/lib/**/*.ts (~68 files including services)
  • src/env.ts
  • src/types/

To apps/api/:

  • app/api/**/*.ts (34 routes)
  • app/v1/**/*.ts (21 routes)

Stays in apps/web/:

  • app/[locale]/* pages
  • app/sign-in/, app/sign-up/
  • app/cli-auth/
  • app/components/
  • messages/ (i18n)
  • public/ (assets)

API URL Strategy

Use subdomain approach: api.vm0.dev

  • Requires new Vercel project (VERCEL_PROJECT_ID_API)
  • CLI and runner need to use new API base URL
  • May need CORS configuration

Special Cases

  1. CLI Auth Flow: /cli-auth/page.tsx is a frontend page that calls API endpoints. The page stays in web, API endpoints move to api.

  2. Database Migrations: Should be owned by @vm0/backend package with a separate db:migrate script.

  3. Global Services Pattern: initServices() must be exported from @vm0/backend and work in both apps.

Research Documents

Full analysis available at:

  • /tmp/deep-dive/web-api-split/research.md
  • /tmp/deep-dive/web-api-split/innovate.md

Created from conversation context using /issue-create

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions