Skip to content

# Context Object

The context object is pgflow’s mechanism for providing handlers with access to platform resources like database connections, environment variables, and service clients. Instead of each handler creating its own connections, pgflow manages these resources centrally and injects them through a standardized context parameter.

API Reference

For complete documentation of all context properties, see the Context API Reference.

Future: Custom Resources

The context system is designed to be extensible. Future versions will support custom resources through dependency injection - you’ll define what resources your handlers need, then provide those resources when calling `EdgeWorker.start()`. This allows handlers to be tested in complete isolation with stubbed resources and input values, maintaining full type safety throughout.

## Why Context Exists

Section titled “Why Context Exists”

Before context, handlers faced several challenges:

1. Resource Management - Each handler needed to create its own database connections and clients
2. Configuration Access - No standardized way to access environment variables
3. Boilerplate Code - Repeated connection setup in every handler
4. Resource Leaks - Risk of not properly closing connections
5. Testing Complexity - Difficult to mock resources for testing

The context object solves these problems by providing pre-configured, managed resources that handlers can use immediately.

## Core Principles

Section titled “Core Principles”

Context follows pgflow’s design philosophy:

1. Zero Configuration - Resources are ready to use without setup
2. Type Safety - Full TypeScript support with proper inference
3. Pre-configured Resources - Depending on platform (Supabase only for now), established connections are provided
4. Platform Agnostic - Context interface can adapt to different platforms
5. Backward Compatible - Existing single-parameter handlers continue to work

## How Context Works

Section titled “How Context Works”

When pgflow executes a handler, it passes two parameters: the input object and the context object.

### Before: Global Resources

Section titled “Before: Global Resources”

- Flows
- Background Jobs

    import { Flow } from '@pgflow/dsl/supabase';import { sql } from '../db.js';import { supabase } from '../supabase-client.js';
    const ProcessUserFlow = new Flow<{ userId: string }>({  slug: 'process_user'})  .step({ slug: 'validate' }, async (input) => {    const [user] = await sql`SELECT * FROM users WHERE id = ${input.userId}`;    const apiKey = Deno.env.get('EXTERNAL_API_KEY');    const { data } = await supabase.auth.getUser();
        return { user };  });

    import { sql } from '../db.js';import { supabase } from '../supabase-client.js';
    async function processOrder(input: { orderId: string }) {  const [order] = await sql`SELECT * FROM orders WHERE id = ${input.orderId}`;  const apiKey = Deno.env.get('EXTERNAL_API_KEY');
      await sendNotification(order, apiKey);  return { processed: true };}

### After: Using Context

Section titled “After: Using Context”

- Flows
- Background Jobs

    import { Flow } from '@pgflow/dsl/supabase';
    const ProcessUserFlow = new Flow<{ userId: string }>({  slug: 'process_user'})  .step({ slug: 'validate' }, async (input, ctx) => {    const [user] = await ctx.sql`SELECT * FROM users WHERE id = ${input.userId}`;    const apiKey = ctx.env.EXTERNAL_API_KEY;    const { data } = await ctx.supabase.auth.getUser();
        return { user };  });

    async function processOrder(input: { orderId: string }, ctx) {  const [order] = await ctx.sql`SELECT * FROM orders WHERE id = ${input.orderId}`;  const apiKey = ctx.env.EXTERNAL_API_KEY;  const { data } = await ctx.supabase.auth.getUser();
      await sendNotification(order, apiKey);  return { processed: true };}

Note

The context parameter is optional. Handlers that don’t need platform resources can omit it for backward compatibility.

## Available Resources

Section titled “Available Resources”

The context object provides access to different resources depending on your platform:

Core Resources (always available):

- `env` - Environment variables
- `shutdownSignal` - Worker shutdown signal for graceful cleanup
- `rawMessage` - Original pgmq message with metadata
- `stepTask` - Flow task details including `run_id`, `step_slug`, `flow_slug`, `task_index`, and typed `input` (flow worker mode only)
- `workerConfig` - Resolved worker configuration

Supabase Platform:

- `sql` - PostgreSQL client (postgres.js)
- `supabase` - Supabase client with service role

See the Context API Reference for complete documentation of each property.

## Using Destructuring for Cleaner Code

Section titled “Using Destructuring for Cleaner Code”

While examples in this documentation use `ctx.` for clarity, destructuring often makes your code more readable:

- Flows
- Background Jobs

    // Destructure only what you need.step({ slug: 'sync_data' }, async (input, { sql, supabase, stepTask }) => {  console.log(`Syncing for run ${stepTask.run_id}`);
      const data = await sql`SELECT * FROM pending_sync WHERE user_id = ${input.userId}`;
      const synced = await supabase    .from('synced_data')    .upsert(data)    .select();
      return { syncedCount: synced.data.length };})

    // Destructure only what you needasync function processOrder(input, { sql, env, rawMessage }) {  console.log(`Processing order ${rawMessage.msg_id}`);
      const [order] = await sql`    SELECT * FROM orders WHERE id = ${input.orderId}  `;
      await sendNotification(order, env.NOTIFICATION_API_KEY);}

Tip

Destructuring makes it immediately clear which resources a handler depends on, improving code readability and testability.

## Summary

Section titled “Summary”

The context object streamlines handler development by:

- Providing ready-to-use platform resources
- Eliminating connection boilerplate
- Centralizing resource access depending on platform
- Maintaining type safety throughout
- Supporting gradual migration of existing code

Context embodies pgflow’s philosophy of being robust yet simple - handlers get powerful capabilities without complexity.