Develop
Local development with the Playcademy Vite plugin
Overview
The Vite plugin automatically starts a local development environment with sandbox and backend servers.
Starting Development
Run your standard Vite dev server:
bun devnpm run devpnpm devyarn devWhat happens next?
The plugin automatically
- Starts local sandbox on port
4321 - Starts backend server on port
8788(if configured) - Shows startup info in console
$ bun devVITE v1.2.3➜ Local: http://localhost:5173/➜ Network: use --host to exposePLAYCADEMY v1.2.3➜ Project: my-project➜ Sandbox: http://localhost:4321/api➜ Backend: http://localhost:5173/api (via 8788)Additional Servers
- Backend line appears if you have custom routes configured
Local Development Servers
Sandbox Server
The sandbox simulates the Playcademy Platform API, using mock data for convenience:
| Feature | Behavior |
|---|---|
| User | Mock user session |
| Credits | Starting balance (1000) |
| Inventory | Empty, persists in sandbox database |
| State | Persists in sandbox database |
| Levels | Starts at level 1 |
Your SDK calls connect to the sandbox during development:
import { PlaycademyClient } from '@playcademy/sdk'
const client = await PlaycademyClient.init() // Connects to sandbox in devSandbox configuration:
playcademy({
sandbox: { recreateDb: true },
})playcademy({
sandbox: { memoryOnly: true },
})playcademy({
sandbox: { seed: false },
})Backend Server
The backend server starts automatically if you have any integrations:
export default {
name: 'My Project',
integrations: {
database: { directory: 'db' },
kv: true,
// Any integration enables the backend
},
}No integrations? No need for a backend server.
Learn More
See the CLI Development Guide for detailed information about the backend server.
User Authentication
In Custom Routes, you can access the authenticated platform user via c.get('playcademyUser'):
export async function GET(c: Context) {
const playcademyUser = c.get('playcademyUser')
if (!playcademyUser) {
return c.json({ error: 'Not authenticated' }, 401)
}
return c.json({
userId: playcademyUser.sub,
message: `Hello, ${playcademyUser.name}!`,
})
}Platform Mode Required
playcademyUser is only populated when:
- The plugin is running in Platform Mode
- Requests are made via
sdk.backend(not plainfetch)
In Standalone Mode, playcademyUser will be null.
Standalone Authentication
For user accounts outside the Playcademy platform, set up the Authentication Integration.
Plugin Modes
The Vite plugin supports two modes:
This simulates the full Playcademy platform experience.
- Mock platform API (sandbox)
- Backend server for integrations
- Platform shell UI with dev badge
playcademy({
mode: 'platform', // Default
})Use this when testing your project without Playcademy platform features.
playcademy({
mode: 'standalone',
})Fast Switching
Press m + enter in your terminal while the Vite dev server is running to toggle between platform and standalone modes.
Timeback
Configure Timeback courses, enrollments, and user roles for local development.
Use mock data for quick iteration, or connect to real Timeback credentials for integration testing.
Coming Soon
A new @timeback/local package is in active development.
Once released, this will provide a significant improvement to the local development experience.
Configuration
playcademy({
timeback: {
// id: auto-generated mock ID (default)
// id: 'tb_abc123' - use a real Timeback ID for integration testing
courses: {
'FastMath:3': 'mock', // 'mock' generates fake course ID or omit the course for the same effect
'FastMath:4': '00000011-0001-0001-0001-000000000001', // or use real course ID for integration testing
},
// role: 'student' (default) - cycle with 't' hotkey
},
})Options
By default, all courses from your playcademy.config.js are automatically enrolled with mock IDs.
export default defineConfig({
plugins: [
playcademy(), // All courses enrolled automatically!
],
})This is the same as:
export default defineConfig({
plugins: [
playcademy({
timeback: {
courses: {
'FastMath:3': 'mock', // must match a course in your config
'FastMath:4': 'mock', // must match a course in your config
},
},
}),
],
})Test specific grade enrollment by excluding courses. Use null or false to exclude:
export default {
name: 'My Project',
integrations: {
timeback: {
courses: [
{ subject: 'FastMath', grade: 3 },
{ subject: 'FastMath', grade: 4 },
{ subject: 'FastMath', grade: 5 },
],
},
},
}export default defineConfig({
plugins: [
playcademy({
timeback: {
courses: {
// FastMath:3 is enrolled by default
'FastMath:4': null, // not enrolled yet
'FastMath:5': null, // not enrolled yet
},
},
}),
],
})The example above simulates a student enrolled in grade 3 only: useful for testing grade progression flows.
Test with a specific school or district by providing organization details:
playcademy({
timeback: {
organization: {
id: 'PLAYCADEMY',
name: 'Playcademy Studios',
type: 'department', // 'school' | 'district' | 'department' | etc.
},
},
})Omit the organization property (or set to 'mock') for a generic Playcademy organization.
Test different user experiences by changing the role.
playcademy({
timeback: {
role: 'teacher',
},
})Role Cycling
You can press t in the terminal to cycle roles when Vite is running.
For integration testing against real Timeback services, provide actual student and course IDs:
playcademy({
timeback: {
id: '00000011-0001-0001-0001-000000000000', // real student sourcedId
courses: {
'FastMath:2': '00000022-0002-0002-0002-000000000002', // real course sourcedId
'FastMath:3': '00000033-0003-0003-0003-000000000003', // real course sourcedId
},
},
})To test against live Timeback services, you must also configure your .env file with credentials:
# Required: Timeback API credentials
TIMEBACK_API_CLIENT_ID=your-client-id
TIMEBACK_API_CLIENT_SECRET=your-client-secret
TIMEBACK_API_AUTH_URL=https://auth.example.com
# Required: OneRoster and Caliper endpoints
TIMEBACK_ONEROSTER_API_URL=https://oneroster.example.com
TIMEBACK_CALIPER_API_URL=https://caliper.example.comSee Timeback Authentication and Endpoints for details.
| Option | Description | Default |
|---|---|---|
id | Timeback student ID | auto-generated mock ID |
role | User role: student, parent, teacher, administrator, guardian | 'student' |
organization | Organization config: 'mock' or { id, name, type } | 'mock' |
courses | Override specific courses: 'mock', real ID, or null/false to exclude | auto-enrolled from config |
Course Keys Must Match
The courses entries (e.g., 'FastMath:3') must match courses defined in your playcademy.config.js:
timeback: {
courses: [
{ subject: 'FastMath', grade: 3 }, // → 'FastMath:3' in vite.config.ts
{ subject: 'FastMath', grade: 4 }, // → 'FastMath:4' in vite.config.ts
],
}Role Cycling
Press t + enter in the terminal to cycle through Timeback roles during development:
12:00:00 PM [playcademy] (timeback) student → parent
12:00:01 PM [playcademy] (timeback) parent → teacher
12:00:02 PM [playcademy] (timeback) teacher → administrator
12:00:03 PM [playcademy] (timeback) administrator → studentLearn More
See the Timeback Integration guide for more on developing with Timeback in your app.
CLI Hotkeys
This plugin provides keyboard shortcuts in the terminal during development:
| Key | Description |
|---|---|
m | Toggle between platform and standalone modes |
d | Recreate sandbox database (resets to fresh seeded state) |
p | Cycle platform user role (player → developer → admin) |
t | Cycle Timeback role (student → parent → teacher → admin → guardian) |
Database Recreation
Some configuration changes require recreating the sandbox database to take effect.
Press d after changing options like timeback.id, timeback.organization, or timeback.courses in your vite.config.ts.
Debugging
Verbose Logging
Enable verbose logging to see all sandbox activity:
playcademy({ sandbox: { verbose: true } })This shows:
- All API calls and responses
- Sandbox startup details
- Full error stack traces
Log Levels
Control the amount of logging with the logLevel option:
playcademy({
sandbox: {
logLevel: 'debug', // 'debug' | 'info' | 'warn' | 'error'
},
})| Log Level | Description |
|---|---|
debug | All logs including detailed debug information |
info | General information (default) |
warn | Warnings and errors only |
error | Errors only |
