11# @workflow/world-postgres
22
3- A PostgreSQL-backed workflow runtime for durable, long-running workflows in JavaScript/TypeScript. Designed for multi-host self-hosted solutions with flexible queue and execution strategies.
3+ An embedded worker/ workflow system backed by PostgreSQL for multi-host self-hosted solutions
44
55## Installation
66
@@ -12,52 +12,54 @@ pnpm add @workflow/world-postgres
1212yarn add @workflow/world-postgres
1313```
1414
15- ## Quick Start (Next.js)
15+ ## Usage
1616
17- Get started in seconds by adding this to your Next.js project:
17+ ### Basic Setup
1818
19- ``` typescript
20- // instrumentation.ts
21- export async function register() {
22- if (process .env .NEXT_RUNTIME !== ' edge' ) {
23- const { createWorld } = await import (" @workflow/world-postgres" );
19+ The postgres world can be configured by setting the WORKFLOW_TARGET_WORLD environment variable to the package name.
2420
25- const world = createWorld ();
26- await world .start ();
27- }
28- }
21+ ``` bash
22+ WORKFLOW_TARGET_WORLD=" @workflow/world-postgres"
2923```
3024
31- Set these environment variables:
25+ ### Configuration
26+
27+ Configure the PostgreSQL world using environment variables:
3228
3329``` bash
34- export WORKFLOW_TARGET_WORLD=" @workflow/world-postgres"
35- export WORKFLOW_POSTGRES_URL=" postgres://username:password@localhost:5432/database"
36- export WORKFLOW_POSTGRES_SECURITY_TOKEN=" your-secret-token-here"
37- export WORKFLOW_POSTGRES_APP_URL=" http://localhost:3000"
30+ WORKFLOW_POSTGRES_URL=" postgres://username:password@localhost:5432/database"
31+ WORKFLOW_POSTGRES_SECURITY_TOKEN=" your-secret-token-here"
32+ WORKFLOW_POSTGRES_APP_URL=" http://localhost:3000"
3833```
3934
40- ** ⚠️ IMPORTANT ** : Always set a strong ` WORKFLOW_POSTGRES_SECURITY_TOKEN ` in production. This token authenticates queue workers when they call your workflow endpoints and prevents unauthorized access.
35+ ### Programmatic Usage
4136
42- That's it! The world will automatically use pg-boss for queuing and HTTP proxy for executing workflows.
37+ You can also create a PostgreSQL world directly in your code:
4338
44- ## Architecture
39+ ``` typescript
40+ import { createWorld , createPgBossQueue } from " @workflow/world-postgres" ;
4541
46- This package provides a layered architecture with three key components:
42+ const world = createWorld ({
43+ connectionString: " postgres://username:password@localhost:5432/database" ,
44+ securityToken: " your-secret-token-here" ,
45+ queueFactorya: createPgBossHttpProxyQueue ({
46+ jobPrefix: " my-app" ,
47+ queueConcurrency: 10 ,
48+ })
49+ });
50+ ```
4751
48- - ** Storage** : Persists workflow state in PostgreSQL (runs, events, steps, hooks, streaming chunks). All tables are isolated in their own PostgreSQL schema.
49- - ** Queue Driver** : Manages job queuing and worker orchestration (default: pg-boss, or bring your own)
50- - ** Proxy Strategy** : Determines how jobs are executed (HTTP calls or direct function invocation)
52+ ** ⚠️ IMPORTANT** : Always set a strong ` WORKFLOW_POSTGRES_SECURITY_TOKEN ` in production. This token authenticates queue workers when they call your workflow endpoints and prevents unauthorized access.
5153
52- ## Execution Patterns
54+ ## Architecture
5355
54- The package supports flexible execution patterns based on two dimensions:
56+ The package supports flexible queues and execution patterns, letting you choose how jobs are queued and where the steps and workflows execution will be happen.
5557
5658### Queue Strategy
5759- ** Built-in pg-boss** (default): Reliable PostgreSQL-backed job queue
5860- ** Custom queue** : Implement your own queue system (Redis, SQS, RabbitMQ, etc.)
5961
60- ### Proxy Strategy
62+ ### Execution Proxy Strategy
6163- ** HTTP Proxy** : Workers call workflow endpoints over HTTP (` /.well-known/workflow/v1/flow ` and ` /.well-known/workflow/v1/step ` )
6264- ** Function Proxy** : Workers invoke workflow/step functions directly in-process
6365
@@ -66,11 +68,11 @@ The package supports flexible execution patterns based on two dimensions:
6668- ** Separate Process** : Dedicated worker process(es) for better isolation and scaling
6769- ** Serverless** : Receive messages from your queue and call a proxy to execute workflows
6870
69- ## Configuration Patterns
71+ ## Advanced Usage
7072
71- ### Pattern 1: pg-boss + HTTP Proxy (Default)
73+ ### pg-boss + HTTP Proxy (Default)
7274
73- The simplest setup - workers make HTTP calls to your application:
75+ The simplest setup - jobs are queued usning pg-boss and workers make HTTP calls to your application:
7476
7577``` typescript
7678import { createWorld } from " @workflow/world-postgres" ;
@@ -100,14 +102,15 @@ const world = createWorld({
100102});
101103```
102104
103- ### Pattern 2: pg-boss + Function Proxy
105+ ### pg-boss + Function Proxy
104106
105- Workers call workflow functions directly in the same process - better performance, simpler deployment:
107+ Jobs are using pg-boss and workers directly call workflow functions in the same process
106108
107109``` typescript
110+ const { setWorld } = await import (' workflow/runtime' );
108111import { createWorld , createPgBossFunctionProxyQueue } from " @workflow/world-postgres" ;
109112
110- // Import entrypoints from your Next.js API routes
113+ // Import entrypoints from your framework API routes
111114import { __wkf_entrypoint as workflowEntrypoint } from ' ./app/.well-known/workflow/v1/flow/route' ;
112115import { __wkf_entrypoint as stepEntrypoint } from ' ./app/.well-known/workflow/v1/step/route' ;
113116
@@ -119,34 +122,17 @@ const world = createWorld({
119122 }),
120123});
121124
122- await world .start ();
123- ```
125+ setWorld (world );
124126
125- ** Required Environment Variables:**
126- ``` bash
127- WORKFLOW_POSTGRES_URL=" postgres://username:password@localhost:5432/database"
128- WORKFLOW_POSTGRES_SECURITY_TOKEN=" your-secret-token-here"
129- ```
130-
131- ** Optional:**
132- All configuration can be passed programmatically:
133-
134- ``` typescript
135- createPgBossFunctionProxyQueue ({
136- stepEntrypoint ,
137- workflowEntrypoint ,
138- connectionString: " postgres://..." ,
139- securityToken: " your-secret-token" ,
140- jobPrefix: " myapp_" ,
141- queueConcurrency: 10 ,
142- })
127+ await world .start ();
143128```
144129
145- ### Pattern 3: Custom Queue Driver
130+ ### Custom Queue Driver + HTTP Proxy
146131
147132Implement your own queue system for maximum flexibility:
148133
149134``` typescript
135+ const { setWorld } = await import (' workflow/runtime' );
150136import { createWorld } from " @workflow/world-postgres" ;
151137import type { QueueDriver , MessageData } from " @workflow/world-postgres/queue-drivers/types" ;
152138
@@ -182,67 +168,12 @@ const world = createWorld({
182168 queueFactory : () => myCustomQueue ,
183169});
184170
185- await world .start ();
186- ```
187-
188- You can use the helper proxies:
189- - ` createHttpProxy({ baseUrl, securityToken }) ` - for HTTP execution
190- - ` createFunctionProxy({ stepEntrypoint, workflowEntrypoint, securityToken }) ` - for in-process execution
191-
192- See ` src/queue-drivers/types.ts ` for the full ` QueueDriver ` interface and ` MessageData ` structure.
193-
194- ## Execution Environment Examples
195-
196- ### Same Process (Next.js instrumentation.ts)
197-
198- Run workers in the same process as your Next.js application:
199-
200- ``` typescript
201- // instrumentation.ts
202- export async function register() {
203- if (process .env .NEXT_RUNTIME !== ' edge' ) {
204- const { createWorld, createPgBossFunctionProxyQueue } =
205- await import (" @workflow/world-postgres" );
206-
207- const { __wkf_entrypoint : workflowEntrypoint } =
208- await import (' ./app/.well-known/workflow/v1/flow/route' );
209- const { __wkf_entrypoint : stepEntrypoint } =
210- await import (' ./app/.well-known/workflow/v1/step/route' );
211-
212- const world = createWorld ({
213- queueFactory : () =>
214- createPgBossFunctionProxyQueue ({
215- stepEntrypoint ,
216- workflowEntrypoint ,
217- }),
218- });
219-
220- await world .start ();
221- }
222- }
223- ```
224-
225- ### Separate Worker Process
226-
227- Run workers in a dedicated process for better isolation:
228-
229- ``` typescript
230- // worker.ts
231- import { createWorld , createPgBossHttpProxyQueue } from " @workflow/world-postgres" ;
232-
233- const world = createWorld ({
234- queueFactory : () =>
235- createPgBossHttpProxyQueue ({
236- baseUrl: " http://localhost:3000" , // Your app URL
237- }),
238- });
171+ setWorld (world );
239172
240173await world .start ();
241174```
242175
243- Then run: ` node worker.ts `
244-
245- ### Serverless
176+ ### Serverless execution
246177
247178In a serverless environment, receive messages from your queue and execute them via proxy:
248179
@@ -333,78 +264,6 @@ All environment variables can be overridden by passing configuration programmati
333264- ** Configurable Concurrency** : Adjustable worker concurrency for queue processing
334265- ** Type-Safe** : Full TypeScript support with exported types
335266
336- ## API Reference
337-
338- ### ` createWorld(options) `
339-
340- Creates a workflow world instance with PostgreSQL storage.
341-
342- ** Options:**
343- - ` connectionString?: string ` - PostgreSQL connection string (default: ` process.env.WORKFLOW_POSTGRES_URL ` )
344- - ` securityToken?: string ` - Token for authenticating queue workers (default: ` process.env.WORKFLOW_POSTGRES_SECURITY_TOKEN ` )
345- - ` queueFactory?: () => QueueDriver ` - Factory function to create queue driver (default: ` createPgBossHttpProxyQueue() ` )
346-
347- ** Returns:** World instance with ` start() ` method
348-
349- ### Built-in Queue Factories
350-
351- #### ` createPgBossHttpProxyQueue(options) `
352-
353- Creates a pg-boss queue driver with HTTP proxy execution.
354-
355- ** Options:**
356- - ` connectionString?: string `
357- - ` securityToken?: string `
358- - ` jobPrefix?: string `
359- - ` queueConcurrency?: number `
360- - ` port?: number `
361- - ` baseUrl?: string `
362-
363- #### ` createPgBossFunctionProxyQueue(options) `
364-
365- Creates a pg-boss queue driver with direct function call execution.
366-
367- ** Options:**
368- - ` stepEntrypoint: (request: Request) => Promise<Response> ` - Required
369- - ` workflowEntrypoint: (request: Request) => Promise<Response> ` - Required
370- - ` connectionString?: string `
371- - ` securityToken?: string `
372- - ` jobPrefix?: string `
373- - ` queueConcurrency?: number `
374-
375- ### Proxy Helpers
376-
377- #### ` createHttpProxy(options) `
378-
379- Creates an HTTP proxy for executing workflows via HTTP calls.
380-
381- ** Options:**
382- - ` baseUrl?: string ` - Base URL of your application
383- - ` port?: number ` - Port (if baseUrl not provided)
384- - ` securityToken: string ` - Security token for authentication
385-
386- #### ` createFunctionProxy(options) `
387-
388- Creates a function proxy for executing workflows via direct function calls.
389-
390- ** Options:**
391- - ` stepEntrypoint: (request: Request) => Promise<Response> ` - Required
392- - ` workflowEntrypoint: (request: Request) => Promise<Response> ` - Required
393- - ` securityToken: string ` - Security token for authentication
394-
395- ## TypeScript Support
396-
397- All public APIs are fully typed. Import types from the package:
398-
399- ``` typescript
400- import type {
401- QueueDriver ,
402- MessageData ,
403- WkfProxy ,
404- PostgresWorldConfig
405- } from " @workflow/world-postgres" ;
406- ```
407-
408267## Development
409268
410269For local development, you can use the included Docker Compose configuration:
0 commit comments