Custom Vue SPA Apps
Custom Vue SPA Apps
When the visual designer is not enough, build a custom SPA app — a real Vue 3 + Vite single-page application that you author locally and publish as a versioned bundle. It is the “infinite customization” end of the spectrum: your code, your components, your UX — still permission-gated and available on the web, inside Plex, and on mobile.
How it works (in 60 seconds)
Your built Vite bundle is published as an app version and served into a sandboxed iframe with an opaque origin. Your code never holds a session or token and cannot reach the network directly (connect-src 'none'). Every platform call goes through the @datamagik/app-sdk bridge, which the trusted host page proxies to app-scoped APIs with server-side authorization on every request. You declare up front which lookup tables, ODBC queries, scripts, serial series, and printers your app uses — undeclared resources are refused (not_declared).
Quickstart
# 1. Create the SPA app shell (Builder permission required)
# apps_create { "app_name": "Receiving", "app_type": "spa" }
# 2. Scaffold a starter (prompts for "simple" or "showcase")
npx @datamagik/cli init my-app
cd my-app && npm install
# 3. Set "appId" in dm.config.json, then log in
dm login --url https://data-magik.com # paste an API key (dcp_...)
# 4. Run it now — hot reload against real data
dm dev --context order=42 --context line=L1
# 5. Declare your resources, regenerate types
dm add
dm typegen
# 6. Publish a version (vite build -> zip dist/ -> upload)
dm publish --message "first SPA version"
The showcase starter ships a platform-styled app with a working example of every SDK area; simple is a minimal connect-and-query app. Full command details are in Installing & Using the CLI.
Rules of the sandbox
- No direct network.
fetch/XHR/WebSocket are blocked by CSP — use the SDK. - No cookies or platform-origin storage. The frame is opaque-origin.
- Relative asset paths only. The CLI forces Vite
base: './'— keep it. - Declared resources only. Add the resource to
dm.config.json, re-rundm typegen, and republish. - Bundle caps: zip ≤ 30 MiB, ≤ 100 MiB uncompressed, ≤ 1,000 files.
Writing app code
import { dm } from './dm.generated';
await dm.init(); // resolves platform context
const rows = await dm.data.query('parts'); // typed rows, alias autocompletes
const sn = await dm.serials.generate('lotSerial', { PART: part });
await dm.print.submit('line1Zebra', { format: 'zpl', data: label });
dm.emitOutput('completed', { received: rows.length });
See every method in The App SDK Reference.
Not yet supported (v1)
Writing rows (dm.data.writeRow) and capture (dm.capture.*) are not part of the v1 SPA SDK surface — those flows currently authorize through editable components in a layout app. Reading lookup data, running scripts, generating serials, and printing are fully supported.