@anushdsouza/world-heroku
v0.1.0
Published
A personal, unofficial community World adapter for running Workflow DevKit applications on Heroku Postgres.
Downloads
48
Maintainers
Readme
@anushdsouza/world-heroku is a personal, unofficial community
World adapter for the
Workflow DevKit on Heroku. It uses Heroku Postgres
for workflow history, steps, hooks, streams, durable jobs, retries, and timers.
The adapter composes the official
@workflow/world-postgres
implementation and adds the provider boundary that Heroku applications need:
- zero-argument configuration from Heroku's
DATABASE_URLconfig var; - Heroku-specific concurrency, pool, and queue-prefix config vars;
- fail-fast production configuration instead of a silent localhost fallback;
- a database bootstrap command designed for Heroku's release phase; and
- Heroku-specific deployment and capacity guidance.
[!IMPORTANT] This is a personal, experimental community project maintained by Anush D'Souza. It is not affiliated with, endorsed by, or supported by Heroku, Salesforce, or Vercel, and it is not a managed durable-execution service. “Heroku” is used only to describe platform compatibility. The current implementation inherits the Postgres World's embedded worker model and its limitations.
Install
npm install workflow @anushdsouza/world-herokuThe package uses its maintainer's npm scope, following the Workflow ecosystem
convention for independently published community Worlds. The @workflow/*
namespace is reserved for packages published by the Workflow project.
Deploy on Heroku
Attach Heroku Postgres to your application, then select the World:
heroku config:set \
WORKFLOW_TARGET_WORLD="@anushdsouza/world-heroku" \
--app your-appHeroku Postgres supplies DATABASE_URL automatically. Bootstrap the Workflow
schema before starting application dynos by adding this command to the
application's release phase:
release: npx --no-install workflow-heroku-bootstrap
web: npm startThe bootstrap is idempotent. A failed release command prevents the new release from replacing the currently running release.
Start the embedded worker
The Postgres-backed World has a background Graphile Worker. Applications must
call world.start() once during server initialization.
For Next.js, add a root instrumentation.ts:
export async function register() {
if (process.env.NEXT_RUNTIME !== 'edge') {
const { getWorld } = await import('workflow/runtime');
const world = await getWorld();
await world.start?.();
}
}Configure Workflow DevKit in next.config.ts as usual:
import type { NextConfig } from 'next';
import { withWorkflow } from 'workflow/next';
const nextConfig: NextConfig = {};
export default withWorkflow(nextConfig);For other long-lived Node.js frameworks, call the same getWorld() and
start() sequence from the framework's server-startup hook. The web process
must expose Workflow DevKit's generated HTTP routes and listen on Heroku's
PORT.
Configuration
createWorld() accepts the same programmatic configuration as the official
Postgres World. With no arguments, it resolves these config vars in order:
| Concern | Preferred variable | Compatible fallback | Default |
| --- | --- | --- | --- |
| PostgreSQL URL | WORKFLOW_HEROKU_POSTGRES_URL | WORKFLOW_POSTGRES_URL, then DATABASE_URL | Required |
| Worker concurrency per process | WORKFLOW_HEROKU_WORKER_CONCURRENCY | WORKFLOW_POSTGRES_WORKER_CONCURRENCY | 10 |
| Internal pool size per process | WORKFLOW_HEROKU_MAX_POOL_SIZE | WORKFLOW_POSTGRES_MAX_POOL_SIZE | 10 |
| Graphile job prefix | WORKFLOW_HEROKU_JOB_PREFIX | WORKFLOW_POSTGRES_JOB_PREFIX | Unset |
All numeric values must be positive integers. Heroku-specific variables win when both forms are set.
Programmatic usage is also supported:
import { createWorld } from '@anushdsouza/world-heroku';
const world = createWorld({
connectionString: process.env.DATABASE_URL!,
jobPrefix: 'billing',
maxPoolSize: 10,
queueConcurrency: 8,
});
await world.start();The package also re-exports the Postgres schema:
import { events, hooks, runs, steps, streams } from '@anushdsouza/world-heroku';
// or: import * as schema from '@anushdsouza/world-heroku/schema';Architecture
Heroku web dyno
├── application server
├── /.well-known/workflow/v1/* execution routes
└── embedded Graphile Worker
│
▼
Heroku Postgres
├── workflow run, event, step, hook, and stream tables
└── durable Graphile jobs, retries, and scheduled wake-upsThe queue worker sends workflow and step execution back to the HTTP server on
the same dyno through loopback. This is why the first release uses one
long-lived web process rather than presenting a separate Heroku worker
process as if it were already supported by the upstream adapter.
Workflow history and queued work survive dyno replacement because they live in Postgres. External side effects can still be delivered at least once; steps that charge a card, send a message, or mutate another system must use domain idempotency keys.
Read the architecture notes before changing dyno formation, connection limits, or worker concurrency.
Operational checklist
Before using this adapter for a production workload:
- Run
workflow-heroku-bootstrapin the release phase. - Confirm server startup calls
world.start()exactly once per process. - Keep
maxPoolSizewithin the database plan's total connection budget after multiplying it by every web process. - Keep worker concurrency within both application and downstream-system capacity.
- Make every external side effect idempotent and safe to retry.
- Define run-history retention and cleanup outside this package.
- Add payload encryption, access control, tenant isolation, quotas, and audit export appropriate to the application.
- Test deploy, restart, database-unavailable, delayed-retry, and hook-resume recovery paths against the intended Heroku formation.
Known limits
- This is a provider adapter over Postgres World, not a new execution engine.
- The embedded worker is coupled to the web process and its loopback execution routes.
- Horizontal web scaling multiplies worker concurrency and database pools.
- General workflow-history retention and cleanup are not implemented upstream.
- Application payload encryption and tenant isolation are not added here.
- Postgres World does not provide Vercel's deployment-affinity semantics.
- Heroku's 30-second graceful-shutdown window still requires idempotent, retry-safe handlers.
Development and verification
Requirements: Node.js 22 or newer and npm 11.
npm install
npm run check
npm run test:integration
npm run release:safetynpm run check validates formatting and lint rules, TypeScript declarations,
unit tests, public package exports, the exact npm tarball contents, and known
dependency vulnerabilities. npm run test:integration builds the package,
starts an isolated PostgreSQL 18 container when TEST_DATABASE_URL is not
already set, verifies that schema bootstrap is idempotent, and runs both the
adapter smoke test and the official Workflow World conformance suite. CI runs
that same real-database gate on Node.js 22, 24, and 26. npm run
release:safety scans the complete public source candidate for credential
patterns, private filesystem paths, unsafe environment files, private keys,
and oversized or unexpected release artifacts.
Release and Workflow community listing
Releases are published from an exact vX.Y.Z tag on main after the full test
gate has passed. The npm package and source repository must both be public
before this World can be submitted to Workflow's ecosystem manifest.
The complete sequence—including the one-time npm bootstrap, trusted
publishing, repository protection, release verification, and the upstream
worlds-manifest.json pull request—is in
docs/publishing.md. The proposed manifest object is
docs/worlds-manifest-entry.json.
See CONTRIBUTING.md for the contribution workflow. The latest evidence and outstanding publication gates are in docs/release-readiness.md.
