> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tasks

> Workflow and background job settings

Sonamu provides a Workflow system for handling asynchronous background jobs. You can separate long-running tasks to the background to improve API response times and distribute work across Worker processes.

## Basic Structure

```typescript theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  tasks: {
    enableWorker: true,
    workerOptions: {
      concurrency: 1,
      usePubSub: true,
      listenDelay: 500,
    },
    contextProvider: (defaultContext) => ({
      ...defaultContext,
      ip: "127.0.0.1",
      session: {},
    }),
  },
  // ...
});
```

## enableWorker

Determines whether to enable the Worker process.

**Type**: `boolean` (optional)

**Default**: `false`

```typescript theme={null}
export default defineConfig({
  tasks: {
    enableWorker: true, // Enable Worker
  },
});
```

**What is a Worker?**

* Processes tasks in a separate process
* No impact on main API server
* Can run multiple Workers for distributed processing

<Tip>
  In production environments, we recommend setting `enableWorker: true` and running a dedicated
  Worker process.
</Tip>

### Control via Environment Variable

```typescript theme={null}
export default defineConfig({
  tasks: {
    enableWorker: !["true", "1"].includes(process.env.DISABLE_WORKER ?? "false"),
  },
});
```

**.env**:

```bash theme={null}
# Disable Worker (development)
DISABLE_WORKER=true

# Enable Worker (production)
DISABLE_WORKER=false
```

## workerOptions

Configures how the Worker operates.

**Type**: `WorkflowOptions` (optional)

```typescript theme={null}
type WorkflowOptions = {
  concurrency?: number;
  usePubSub?: boolean;
  listenDelay?: number;
};
```

### concurrency

The number of tasks the Worker will process simultaneously.

**Type**: `number`

**Default**: `1`

```typescript theme={null}
export default defineConfig({
  tasks: {
    workerOptions: {
      concurrency: 1, // Process 1 task at a time
    },
  },
});
```

**Recommended values**:

* `1` - Simple tasks, sequential processing required
* `2-5` - General background tasks
* `10+` - Lightweight I/O-bound tasks

<Note>
  Higher `concurrency` processes more tasks simultaneously but increases CPU and memory usage.
</Note>

### usePubSub

Uses Pub/Sub for distributing work among multiple Workers.

**Type**: `boolean`

**Default**: `true`

```typescript theme={null}
export default defineConfig({
  tasks: {
    workerOptions: {
      usePubSub: true, // Use PostgreSQL LISTEN/NOTIFY
    },
  },
});
```

**Behavior**:

* `false`: Each Worker polls independently
* `true`: New task notifications propagated instantly via PostgreSQL LISTEN/NOTIFY

<Tip>
  Pub/Sub uses PostgreSQL's built-in LISTEN/NOTIFY mechanism, so no additional infrastructure (e.g.,
  Redis) is required. It leverages the existing database connection.
</Tip>

### listenDelay

The interval (in milliseconds) for checking tasks.

**Type**: `number` (ms)

**Default**: `500` (0.5 seconds)

```typescript theme={null}
export default defineConfig({
  tasks: {
    workerOptions: {
      listenDelay: 500, // Check every 0.5 seconds
    },
  },
});
```

**Recommended values**:

* `100-500ms` - Tasks requiring fast response
* `500-1000ms` - General background tasks
* `1000-5000ms` - Non-urgent tasks

## contextProvider

Creates the Context for tasks running in the Worker.

**Type**: `(defaultContext) => Context | Promise<Context>`

```typescript theme={null}
export default defineConfig({
  tasks: {
    contextProvider: (defaultContext) => {
      return {
        ...defaultContext,
        ip: "127.0.0.1",
        session: {},
      };
    },
  },
});
```

**defaultContext includes**:

* `reply` - Fastify reply object (null in worker)
* `request` - Fastify request object (null in worker)
* `headers` - Request headers
* `createSSE` - SSE stream creator
* `naiteStore` - Naite logging store

### Custom Context

```typescript theme={null}
export default defineConfig({
  tasks: {
    contextProvider: (defaultContext) => {
      return {
        ...defaultContext,
        ip: "worker",
        session: {},
        config: {
          apiUrl: process.env.API_URL,
        },
        logger: console,
      };
    },
  },
});
```

<Warning>
  In Worker Context, `request` and `reply` are `null`. HTTP-related features cannot be used.
</Warning>

## Basic Examples

### Single Worker (Development)

```typescript theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  tasks: {
    enableWorker: process.env.NODE_ENV === "production",
    workerOptions: {
      concurrency: 1,
      usePubSub: false, // Single Worker
      listenDelay: 500,
    },
    contextProvider: (defaultContext) => ({
      ...defaultContext,
      ip: "127.0.0.1",
      session: {},
    }),
  },
});
```

### Multiple Workers (Production)

```typescript theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  tasks: {
    enableWorker: true,
    workerOptions: {
      concurrency: 5,
      usePubSub: true, // Distribute via PostgreSQL LISTEN/NOTIFY
      listenDelay: 500,
    },
    contextProvider: (defaultContext) => ({
      ...defaultContext,
      ip: "worker",
      session: {},
    }),
  },
});
```

## Practical Examples

### Environment-based Configuration

```typescript theme={null}
import { defineConfig } from "sonamu";

const isDev = process.env.NODE_ENV === "development";
const isProd = process.env.NODE_ENV === "production";

export default defineConfig({
  tasks: {
    // Development: No Worker (process in main)
    // Production: Enable Worker
    enableWorker: isProd,

    workerOptions: {
      concurrency: isProd ? 5 : 1,
      usePubSub: isProd,
      listenDelay: isDev ? 100 : 500, // Development: fast response
    },

    contextProvider: (defaultContext) => ({
      ...defaultContext,
      ip: isProd ? "worker" : "127.0.0.1",
      session: {},
    }),
  },
});
```

### High-Load Workflow

```typescript theme={null}
export default defineConfig({
  tasks: {
    enableWorker: true,
    workerOptions: {
      concurrency: 10, // 10 concurrent tasks
      usePubSub: true, // Multiple Worker support
      listenDelay: 1000, // 1 second interval
    },
    contextProvider: (defaultContext) => ({
      ...defaultContext,
      ip: "worker",
      session: {},
      logger: {
        info: (msg) => console.log(`[Worker] ${msg}`),
        error: (msg) => console.error(`[Worker Error] ${msg}`),
      },
    }),
  },
});
```

### Custom Context

```typescript theme={null}
export default defineConfig({
  tasks: {
    enableWorker: true,
    workerOptions: {
      concurrency: 3,
      usePubSub: true,
      listenDelay: 500,
    },
    contextProvider: async (defaultContext) => {
      // Async initialization
      const config = await loadConfig();

      return {
        ...defaultContext,
        ip: "worker",
        session: {},
        config,
        services: {
          email: new EmailService(),
          notification: new NotificationService(),
        },
      };
    },
  },
});
```

## Using Workflows

After tasks configuration, define background jobs with the `workflow()` function.

```typescript theme={null}
import { workflow } from "sonamu";

export const sendWelcomeEmail = workflow(
  { name: "send_welcome_email" },
  async ({ input, step }) => {
    const user = await step
      .define({ name: "fetch_user" }, async () => {
        return await UserModel.findById(input.userId);
      })
      .run();

    await step
      .define({ name: "send_email" }, async () => {
        await sendEmail(user.email, "Welcome!");
      })
      .run();
  },
);
```

**Calling**:

```typescript theme={null}
import { Sonamu } from "sonamu";

// Runs in background
await Sonamu.workflows.run({ name: "send_welcome_email", version: null }, { userId: 123 });
```

→ [Workflow Usage](/en/advanced-features/workflows/workflow-decorator)

## Running Workers

### Development Environment

In development, keep `enableWorker: false` and process in main:

```bash theme={null}
pnpm dev
```

### Production Environment

Run a dedicated Worker process:

```bash theme={null}
# API server
pnpm start

# Worker process (separate terminal)
pnpm start:worker
```

**package.json**:

```json theme={null}
{
  "scripts": {
    "start": "node dist/server.js",
    "start:worker": "ENABLE_WORKER=true node dist/worker.js"
  }
}
```

### Multiple Workers

Run multiple Workers to increase throughput:

```bash theme={null}
# Worker 1
ENABLE_WORKER=true node dist/worker.js

# Worker 2
ENABLE_WORKER=true node dist/worker.js

# Worker 3
ENABLE_WORKER=true node dist/worker.js
```

<Tip>Using PM2 or Docker makes it easy to manage multiple Workers.</Tip>

## Pub/Sub Behavior

When `usePubSub: true` (the default), the Worker receives new task notifications using PostgreSQL's built-in `LISTEN`/`NOTIFY` mechanism. No separate message broker (e.g., Redis) is required — it leverages the existing database connection.

```typescript theme={null}
export default defineConfig({
  tasks: {
    enableWorker: true,
    workerOptions: {
      usePubSub: true, // Use PostgreSQL LISTEN/NOTIFY (default)
    },
  },
});
```

## Cautions

### 1. Worker Context Limitations

```typescript theme={null}
// ❌ Bad: Trying HTTP response in Worker
export const processData = workflow({ name: "process_data" }, async () => {
  const ctx = Sonamu.getContext();
  ctx.reply.send({ done: true }); // null in Worker!
});

// ✅ Good: Save results to DB
export const processData = workflow({ name: "process_data" }, async () => {
  const result = await heavyProcessing();
  await ResultModel.save({ data: result });
});
```

### 2. enableWorker Setting

```typescript theme={null}
// ❌ Bad: No Worker in production
enableWorker: false; // All tasks run in main process!

// ✅ Good: Different per environment
enableWorker: process.env.NODE_ENV === "production";
```

### 3. Excessive concurrency

```typescript theme={null}
// ❌ Bad: Too high concurrency
workerOptions: {
  concurrency: 100,  // Risk of CPU and memory shortage
}

// ✅ Good: Appropriate level
workerOptions: {
  concurrency: 5,  // Match server resources
}
```

## Next Steps

After completing Task configuration:

* [workflows](/en/advanced-features/workflows/workflow-decorator) - Defining Workflows
* [steps](/en/advanced-features/workflows/steps) - Separating work into Steps
* [error-handling](/en/advanced-features/workflows/error-handling) - Error handling and retries
