Frontend Integration (Vite)

Who this is for

You want DeskFlow’s task board UI in frontend/ — calling task.list through Vite’s dev proxy and shipping a production build into public/ beside the Moonlight API.

What you will learn

  • Scaffold commands for new and existing apps (--react-ts, frontend:scaffold)
  • The two-terminal dev workflow (API 8000, Vite 5173)
  • Production build, cleanup commands, and default CORS for local SPA

Before you start

Before you start
  • Booted DeskFlow API (php pionia serve on port 8000)
  • Node.js 18+ installed
  • Frontend overview — when to add a SPA to an API-first app

How it works

Pionia is API-first. v3 adds first-class Vite scaffolding — React, Vue, and Svelte templates with a dev proxy to /api/v1/.

flowchart TB subgraph dev [Development] Vite[Vite dev server :5173] Pionia[Pionia php pionia serve :8000] Vite -->|"/api/* proxy"| Pionia end subgraph prod [Production] Build[php pionia frontend:build] Build --> Index[public/index.html] Index --> API2["/api/v1/ same host"] end

Scaffold on a new project

Create the app, then add a frontend in the same session:

composer create-project pionia/pionia-app my-api -- --react-ts
cd my-api
php pionia serve

Or scaffold the frontend after the API exists:

composer create-project pionia/pionia-app my-api
cd my-api
php pionia frontend:scaffold --framework=react-ts --yes

Pass a framework flag to composer create-project after -- (e.g. -- --vue-ts), or use frontend:scaffold on an existing app.

Framework flagStack
react-tsReact + TypeScript
vue-tsVue + TypeScript
svelte-tsSvelte + TypeScript

Creates frontend/ with Vite config proxying API calls to your Pionia dev server.

Development

Terminal 1 — API:

php pionia serve

Terminal 2 — Vite:

php pionia frontend:dev

DeskFlow SPA example — list tasks via relative path (works in dev proxy and production):

const res = await fetch('/api/v1/', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ service: 'task', action: 'list', status: 'open' }),
});

After member.login, attach the JWT:

headers: {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${token}`,
}

Production build

php pionia frontend:build

Assets land in public/assets/; SPA entry is served via SPA fallback when public/index.html exists (see [frontend] SPA_FALLBACK in settings.ini).

Cleanup

php pionia frontend:clean   # remove build output
php pionia frontend:drop    # remove frontend/ tree

CORS

Default scaffold includes [cors] for http://localhost:5173 in settings.ini.

Common mistakes

  • Using absolute http://127.0.0.1:8000/api/v1/ in fetch — breaks after frontend:build on another host; prefer /api/v1/
  • Skipping frontend:build before deploy — visitors get the API welcome page instead of the SPA
  • Running frontend:drop without backing up custom components — the command removes the entire frontend/ tree
  • Testing authenticated routes without CORS headers for Authorization — verify [cors] allows your dev origin

What’s next

Authentication

member.login and Bearer tokens in the SPA.

API path helpers

apiPingPath() and apiVersionPath().

RoadRunner

Run API + workers in production.