ViteHub

Run a sandbox

Call a discovered sandbox from Vite or Nitro server code and return a safe application response.

This guide focuses on the route-side call. It assumes Sandbox is already registered and a release-notes definition exists.

Call pattern

Every route follows the same shape:

  1. Read or build a payload.
  2. Call runSandbox(name, payload).
  3. Check result.isErr().
  4. Return result.value.
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 }
})

Pass request context

Use context for metadata that should not be part of the business payload:

const result = await runSandbox('release-notes', payload, {
  context: {
    requestId: event.context.requestId,
  },
})

The sandbox receives it as the second argument:

export default defineSandbox(async (payload: ReleaseNotesPayload = {}, context = {}) => {
  return {
    requestId: context.requestId,
    summary: payload.notes?.split('\n')[0] || '',
  }
})

Verify the route

curl -X POST http://localhost:3000/api/release-notes \
  -H 'content-type: application/json' \
  -d '{"notes":"- Added route-side Sandbox call"}'

Expected response:

{
  "result": {
    "summary": "Added route-side Sandbox call",
    "items": [
      "Added route-side Sandbox call"
    ]
  }
}

Avoid these mistakes

MistakeFix
Reading result.value before checking isErr()Return or throw from the error branch first.
Putting provider logic in the routePut provider, credentials, and bindings in config.
Passing raw user input directlyValidate with Validate payloads.
Copyright © 2026