Sandbox quickstart
Register Sandbox, define a release-notes sandbox, call it from a route, and verify the JSON result.
This guide creates one release-notes sandbox. The route accepts markdown-style notes, runs the sandbox in an isolated provider runtime, and returns a normalized JSON result.
The provider is the only part that changes between Cloudflare and Vercel. The sandbox definition and route call stay the same.
Prompt
Set up @vitehub/sandbox in this app.
- Install @vitehub/sandbox and one provider SDK
- Register hubSandbox() for Vite or @vitehub/sandbox/nitro for Nitro
- Configure sandbox.provider as cloudflare or vercel
- Define release-notes as a discovered sandbox
- Call runSandbox('release-notes', payload) from a route
- Handle result.isErr() before reading result.value
Docs: /docs/vite/sandbox/quickstart or /docs/nitro/sandbox/quickstart
Install Sandbox
pnpm add @vitehub/sandbox
Install the provider SDK for the platform that will execute the sandbox:
pnpm add @cloudflare/sandbox
pnpm add @vercel/sandbox
Register the integration
Register the Nitro module and choose the provider:
nitro.config.ts
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/sandbox/nitro'],
sandbox: {
provider: 'cloudflare',
},
})
nitro.config.ts
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/sandbox/nitro'],
sandbox: {
provider: 'vercel',
},
})
Define the sandbox
Create a discovered Nitro sandbox file:
server/sandboxes/release-notes.ts
import { defineSandbox } from '@vitehub/sandbox'
export type ReleaseNotesPayload = {
notes?: string
}
export type ReleaseNotesResult = {
summary: string
items: string[]
}
export default defineSandbox(async (payload: ReleaseNotesPayload = {}): Promise<ReleaseNotesResult> => {
const items = (payload.notes || '')
.split('\n')
.map(note => note.replace(/^[-*]\s*/, '').trim())
.filter(Boolean)
return {
summary: items[0] || '',
items,
}
})
Call the sandbox from a route
Add a Nitro route that reads the request body and executes the named sandbox:
server/api/release-notes.post.ts
import { readRequestPayload, runSandbox } from '@vitehub/sandbox'
import type { ReleaseNotesPayload } from '../sandboxes/release-notes'
export default defineEventHandler(async (event) => {
const payload = await readRequestPayload<ReleaseNotesPayload>(event, { notes: '' }) as ReleaseNotesPayload
const result = await runSandbox('release-notes', payload)
if (result.isErr()) {
throw createError({ statusCode: 500, statusMessage: result.error.message })
}
return { result: result.value }
})
Verify the response
Run or deploy the app with a configured provider, then send a request:
curl -X POST http://localhost:3000/api/release-notes \
-H 'content-type: application/json' \
-d '{"notes":"- Added weekly digest\n- Tightened signup copy"}'
The route returns the sandbox result:
{
"result": {
"summary": "Added weekly digest",
"items": [
"Added weekly digest",
"Tightened signup copy"
]
}
}
::

