Heximon Logo
Deploy

Vercel

VercelFunctionsStrategy, VercelEdgeStrategy, .vercel/output Build Output API v3, vercel-entry.js, cron and queue triggers, the freeze-safe workflow drain.

Deploying to Vercel gets you Functions by default — a real Node.js runtime, cron, queues, and durable workflows — with Edge Functions available as an opt-in tier for latency-sensitive routes. One line picks the strategy:

heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { VercelFunctionsStrategy } from "@heximon/vercel";
import { HttpPlugin } from "@heximon/http/compiler";

export default defineHeximonConfig({
  platform: new VercelFunctionsStrategy(),
  plugins: [new HttpPlugin()],
});

VercelFunctionsStrategy is a node-class strategy, platform name "vercel". VercelEdgeStrategy is the edge-class alternative for a V8 isolate tier — swap it in for a controller-level split (edge reads next to a node tier that owns the database) rather than the whole platform; see Mixed-runtime split.

Build and deploy

vp build writes a complete Vercel Build Output API v3 tree at the project root — no Nitro in between:

.vercel/output/
  config.json                              # { version: 3, routes, crons[] }
  functions/__server.func/
    index.js                               # self-contained bundle (deps bundled, no node_modules)
    .vc-config.json                        # nodejs22.x + handler + launcherType
  static/

By default the function is self-contained (bundleDependencies, on by default): every JS dependency is bundled into index.js, so there's no node_modules symlink to resolve — a vercel deploy --prebuilt .func can't follow one anyway. Opt out with new VercelFunctionsStrategy({ bundleDependencies: false }) to keep the standard externalized Node SSR shape instead.

Ship it with either command:

vercel deploy --prebuilt          # add --prod for production

Both host strategies implement the @heximon/deploy capability, so heximon deploy runs the same command for you — plus a vercel whoami preflight, a first-deploy linkage gate (halting with the one-time vercel link command on a fresh project), and a fail-closed CRON_SECRET gate that halts the deploy before upload if your app declares any cron route but no CRON_SECRET is set.

Cron

List SchedulePlugin and the plugin selects VercelScheduleDriver — a clockless driver the platform triggers over HTTP. Each discovered cron gets its own config.json entry, guarded by Authorization: Bearer <CRON_SECRET>:

{ "crons": [{ "path": "/_vercel/cron/0", "schedule": "0 3 * * *" }] }
import type { ScheduledHandler } from "@heximon/schedule";

export class NightlyCleanup implements ScheduledHandler<"0 3 * * *", { name: "Nightly cleanup" }> {
  public handle(scheduledAt: Date): void {
    // …
  }
}

Cron works on both Functions and Edge — the route is plain HTTP either way.

Queues

List QueuePlugin and bind VercelQueue (from @heximon/queue/vercel) as your Queue provider. The queue plugin selects VercelQueueConsumer as the push-drain consumer and emits it as a separate, isolated function (a Build Output API .func can't import across a .func boundary, so the consumer ships its own copy of the server bundle):

import { Module } from "@heximon/runtime";
import { Queue } from "@heximon/queue";
import { VercelQueue } from "@heximon/queue/vercel";

export class AppModule extends Module({
  providers: [{ provide: Queue, useValue: new VercelQueue() }],
  queue: { handlers: [OrdersHandler] },
}) {}

Vercel Queues (V3) topic names allow only [A-Za-z0-9_-] — no dots — and vp build fails with a clear message if an emitted channel name violates that. VercelEdgeStrategy.supportedFeatures.queue stays false — edge isolates have no durable queue support. Vercel Queue delivers back to the same deployment only; for fan-out to other services see Cross-service events.

Durable workflows and saga timers

Vercel Functions freeze between invocations — there's no long-lived process to run a poller on — so a workflow's step.sleep and a saga's TimeoutHandler deadline need a wakeup that doesn't depend on one.

The default is a once-per-minute drain cron vp build folds into config.json; each tick, the generated entry (guarded by CRON_SECRET) re-enters every run whose sleep has elapsed — sub-minute precision on Pro+, but Hobby caps cron at once a day and rejects a * * * * * schedule at deploy time.

The opt-in alternative swaps the drain cron for a delayed Vercel Queue message that wakes each timer at (roughly) its exact deadline instead — sub-second precision, works on Hobby, no minute-cron gate:

examples/flagship/apps/vercel/heximon.config.ts
export default defineHeximonConfig({
  platform: new VercelFunctionsStrategy({ timerWakeup: "queue" }),
  plugins: [
    new HttpPlugin(),
    new QueuePlugin(),
    new SchedulePlugin(),
    new SagaPlugin(),
    new WorkflowPlugin(),
    // …
  ],
});

The switch governs both consumers at once and the app binds a DeferredWakeupQueue satisfier per environment (MemoryDeferredWakeupQueue in dev/CI, VercelDeferredWakeupQueue in production). For the replay engine, the durable_timers mechanics, and the generated drain entry, see Workflows.

Database

Vercel Functions freeze between invocations and don't hold an in-memory connection pool, so your database needs to be edge-capable: a persistent, edge-reachable store like Turso (@libsql/client, bundled to its pure-JS web build) or Neon's serverless Postgres driver. A native TCP driver that can't bundle into a self-contained .func, or an in-memory store that won't survive the freeze, doesn't work here.

Verification

Per the live-deploy verification ledger: the durable Vercel Queue round-trip (POST → platform push → Turso write → GET), the timerWakeup: "queue" wakeup for both workflow step.sleep and saga timeouts, and the drain-cron leg re-entering a suspended workflow run are each verified on a real vercel deploy.

Everything else on this page — cron triggers, the Build Output API shape, the mixed-runtime routing — is build-tested and expected to work but not separately live-verified.

See also

  • WorkflowsWorkflow, step.do, step.sleep, and the replay engine.
  • SagasTimeoutHandler deadlines and DurableTimer.
  • Cross-service events — fan an integration event out past one deployment.
  • Mixed-runtime split — the runtime: "edge" controller partitioning in depth.
  • heximon deploy — the CLI's Vercel-specific preflight, --create, and --provision flags.
Copyright © 2026