Frontend Integration (Vite)

Who this is for

You want Pionia Shop’s storefront UI in frontend/ — calling product.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 Pionia Shop 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

Pionia Shop 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 customer.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

customer.login and Bearer tokens in the SPA.

API path helpers

apiPingPath() and apiVersionPath().

RoadRunner

Run API + workers in production.