# Tasks

> Nitro tasks allow on-off operations in runtime.

## Opt-in to the experimental feature

<important>

Tasks support is currently experimental.
See [nitrojs/nitro#1974](https://github.com/nitrojs/nitro/issues/1974) for the relevant discussion.

</important>

In order to use the tasks API you need to enable experimental feature flag.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  experimental: {
    tasks: true
  }
})
```

## Define tasks

Tasks can be defined in `tasks/[name].ts` files.

Nested directories are supported. The task name will be joined with `:`. (Example: `tasks/db/migrate.ts` task name will be `db:migrate`)

**Example:**

```ts [tasks/db/migrate.ts]
export default defineTask({
  meta: {
    name: "db:migrate",
    description: "Run database migrations",
  },
  run({ payload, context }) {
    console.log("Running DB migration task...");
    return { result: "Success" };
  },
});
```

### Task interface

The `defineTask` helper accepts an object with the following properties:

- **meta** (optional): An object with optional `name` and `description` string fields used for display in the dev server and CLI.
- **run** (required): A function that receives a [`TaskEvent`](#taskevent) and returns (or resolves to) an object with an optional `result` property.

```ts
interface Task<RT = unknown> {
  meta?: { name?: string; description?: string };
  run(event: TaskEvent): { result?: RT } | Promise<{ result?: RT }>;
}
```

### `TaskEvent`

The `run` function receives a `TaskEvent` object with the following properties:

- **name**: The name of the task being executed.
- **payload**: An object (`Record<string, unknown>`) containing any data passed to the task.
- **context**: A `TaskContext` object (may include `waitUntil` depending on the runtime).

```ts
interface TaskEvent {
  name: string;
  payload: TaskPayload;
  context: TaskContext;
}
```

### Registering tasks via config

In addition to file-based scanning, tasks can be registered directly in the Nitro config. This is useful for tasks provided by modules or pointing to custom handler paths.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  experimental: {
    tasks: true
  },
  tasks: {
    "db:migrate": {
      handler: "./tasks/custom-migrate.ts",
      description: "Run database migrations"
    }
  }
})
```

If a task is both scanned from the `tasks/` directory and defined in the config, the config-defined `handler` takes precedence.

## Scheduled tasks

You can define scheduled tasks using Nitro configuration to automatically run after each period of time.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  scheduledTasks: {
    // Run `cms:update` task every minute
    '* * * * *': ['cms:update'],
    // Run a single task (string shorthand)
    '0 * * * *': 'db:cleanup'
  }
})
```

The `scheduledTasks` config maps cron expressions to either a single task name (string) or an array of task names. When multiple tasks are assigned to the same cron expression, they run in parallel.

<tip>

You can use [crontab.guru](https://crontab.guru/) to easily generate and understand cron tab patterns.

</tip>

When a scheduled task runs, it automatically receives a `payload` with `scheduledTime` set to the current timestamp (`Date.now()`).

### Platform support

- **dev**, **node_server**, **node_cluster**, **node_middleware**, **bun** and **deno_server** presets are supported with the [croner](https://croner.56k.guru/) engine.
- **cloudflare_module** and **cloudflare_pages** presets have native integration with [Cron Triggers](https://developers.cloudflare.com/workers/configuration/cron-triggers/). Nitro automatically generates the cron triggers in the wrangler config at build time - no manual wrangler setup required.
- **vercel** preset has native integration with [Vercel Cron Jobs](https://vercel.com/docs/cron-jobs). Nitro automatically generates the cron job configuration at build time - no manual `vercel.json` setup required. You can secure cron endpoints by setting the `CRON_SECRET` environment variable.
- More presets (with native primitives support) are planned to be supported!

## `waitUntil`

When running background tasks, you might want to make sure the server or worker waits until the task is done.

An optional `context.waitUntil` function *might* be available depending on the runtime.

```ts
export default defineTask({
  run({ context }) {
    const promise = fetch(...)
    context.waitUntil?.(promise);
    await promise;
    return { result: "Success" };
  },
});
```

## Programmatically run tasks

To manually run tasks, you can use `runTask(name, { payload?, context? })` utility from `nitro/task`.

**Example:**

```ts [api/migrate.ts]
import { defineHandler } from "nitro";

export default defineHandler(async (event) => {
  // IMPORTANT: Authenticate user and validate payload!
  const payload = Object.fromEntries(event.url.searchParams);
  const { result } = await runTask("db:migrate", { payload });

  return { result };
});
```

### Error handling

`runTask` throws an HTTP error if:

- The task does not exist (status `404`).
- The task has no handler implementation (status `501`).

Any errors thrown inside the task's `run` function will propagate to the caller.

## Run tasks with dev server

Nitro's built-in dev server exposes tasks to be easily executed without programmatic usage.

### Using API routes

#### `/_nitro/tasks`

This endpoint returns a list of available task names and their meta.

```json
// [GET] /_nitro/tasks
{
  "tasks": {
    "db:migrate": {
      "description": "Run database migrations"
    },
     "cms:update": {
      "description": "Update CMS content"
    }
  },
  "scheduledTasks": [
    {
      "cron": "* * * * *",
      "tasks": [
        "cms:update"
      ]
    }
  ]
}
```

#### `/_nitro/tasks/:name`

This endpoint executes a task. You can provide a payload using both query parameters and body JSON payload. The payload sent in the JSON body payload must be under the `"payload"` property.

<code-group>

```ts [tasks/echo/payload.ts]
export default defineTask({
  meta: {
    name: "echo:payload",
    description: "Returns the provided payload",
  },
  run({ payload, context }) {
    console.log("Running echo task...");
    return { result: payload };
  },
});
```

```json [GET]
// [GET] /_nitro/tasks/echo:payload?field=value&array=1&array=2
{
  "field": "value",
  "array": ["1", "2"]
}
```

```json [POST]
/**
 * [POST] /_nitro/tasks/echo:payload?field=value
 * body: {
 *   "payload": {
 *     "answer": 42,
 *     "nested": {
 *       "value": true
 *     }
 *   }
 * }
 */
{
  "field": "value",
  "answer": 42,
  "nested": {
    "value": true
  }
}
```

</code-group>

<note>

The JSON payload included in the body will overwrite the keys present in the query params.

</note>

### Using CLI

<important>

It is only possible to run these commands while the **dev server is running**. You should run them in a second terminal.

</important>

#### List tasks

```sh
nitro task list
```

#### Run a task

```sh
nitro task run db:migrate --payload "{}"
```

The `--payload` flag accepts a JSON string that will be parsed and passed to the task. If the value is not a valid JSON object, the task runs without a payload.

## Notes

### Concurrency

Each task can have **one running instance**. Calling a task of same name multiple times in parallel, results in calling it once and all callers will get the same return value.

<note>

Nitro tasks can be running multiple times and in parallel.

</note>
