Skip to content

Tasks

Tasks

Tasks are classes that extend the Task base class to handle scheduled events triggered by cron expressions. The Task class provides access to environment bindings and execution context for processing scheduled operations.

Every task implementation must provide a handle() method that receives an Event object containing scheduling information. Tasks run independently of HTTP services and have full access to configured environment bindings.

Prerequisites

  • Understanding of TypeScript classes and async/await patterns
  • Familiarity with cron expression syntax
  • Knowledge of Raindrop manifest configuration

Configuration

To add a task to your Raindrop project:

  1. Define the task in your application manifest
  2. Run raindrop build generate to create the necessary files
application "demo-app" {
task "cleanup-task" {
cron = "0 2 * * *" // Daily at 2 AM UTC
type = "cron"
}
}

Once deployed, Raindrop automatically:

  • Schedules your task according to the cron expression
  • Sets up all required bindings
  • Makes environment variables accessible to your task

After running generate, you’ll find a new index.ts file for the task with the basic implementation for you to extend.

Task Class

Extend the Task class and implement the required handle() method:

abstract class Task<Env> {
ctx: ExecutionContext;
env: Env;
constructor(ctx: ExecutionContext, env: Env);
abstract handle(event: Event): Promise<void>;
}

Manifest Configuration

Define cron schedules in your raindrop.manifest:

application "my-app" {
task "cleanup-task" {
cron = "0 2 * * *" // Daily at 2 AM UTC
}
}

Cron Expression Syntax

Cron expressions use five fields: minute hour day-of-month month day-of-week

  • "0 */6 * * *" - Every 6 hours
  • "15 14 1 * *" - 2:15 PM on first day of each month
  • "0 22 * * 1-5" - 10 PM weekdays only
  • "*/10 * * * *" - Every 10 minutes

Implementation

Implement the handle() method in your task class:

import { Task, Event } from '@liquidmetal-ai/raindrop-framework';
import { Env } from './raindrop.gen';
export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
console.log(`Task executed at ${new Date(event.scheduledTime)}`);
console.log(`Triggered by cron: ${event.cron}`);
// Access environment bindings
const db = this.env.DATABASE;
const kv = this.env.KEY_VALUE_STORE;
// Task implementation logic
console.log('Task completed');
}
}

Event Interface

The handle() method receives an Event object with scheduling information:

interface Event {
/** The cron expression that triggered this event */
cron: string;
/** Always "scheduled" for cron-triggered events */
type: 'scheduled';
/** Unix timestamp when the event was scheduled to run */
scheduledTime: number;
}

Event Properties

  • cron: The cron expression that triggered the event
  • type: Always 'scheduled' for cron-triggered tasks
  • scheduledTime: Unix timestamp of the scheduled execution time

Execution Context

Tasks have access to ExecutionContext for managing asynchronous operations:

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
// Ensure background work completes before task ends
const backgroundWork = this.processData();
this.ctx.waitUntil(backgroundWork);
// Continue with synchronous logic
await this.updateStatus();
}
}

Environment Bindings

Access configured resources through the env property:

Database Access

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
const db = this.env.DATABASE;
const result = await db.prepare('SELECT COUNT(*) as count FROM users').first();
console.log('User count:', result.count);
}
}

KV Store Access

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
const kv = this.env.CONFIG_STORE;
const config = await kv.get('app-config');
console.log('Config loaded:', config);
}
}

Queue Access

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
const queue = this.env.WORK_QUEUE;
await queue.send({ type: 'scheduled-work', timestamp: event.scheduledTime });
}
}

Bucket Access

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
const bucket = this.env.FILE_STORAGE;
const file = await bucket.get('status.json');
if (file) {
const content = await file.text();
console.log('File content:', content);
}
}
}

Service Binding Access

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
try {
const response = await this.env.DATA_SERVICE.fetch('https://service/api/status');
if (response.ok) {
const data = await response.json();
console.log('Service status:', data.status);
} else {
console.error('Service call failed:', response.status);
}
} catch (error) {
console.error('Error calling service:', error);
throw error;
}
}
}

Error Handling

Implement error handling in the handle() method:

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
try {
// Task logic that might fail
await this.performCriticalOperation();
} catch (error) {
console.error('Task failed:', error);
// Tasks are automatically retried on failure
throw error;
}
}
}

Using Scheduled Time

Access the scheduled execution time for conditional logic:

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
const scheduledDate = new Date(event.scheduledTime);
const dayOfWeek = scheduledDate.getUTCDay();
const hour = scheduledDate.getUTCHours();
// Conditional execution based on timing
if (dayOfWeek === 0 || dayOfWeek === 6) {
console.log('Weekend execution');
return;
}
console.log(`Task executed at ${scheduledDate.toISOString()}`);
}
}

State Persistence

Use KV stores to maintain state between executions:

export default class extends Task<Env> {
async handle(event: Event): Promise<void> {
const kv = this.env.TASK_STATE;
// Read previous state
const lastRun = await kv.get('last-execution');
const count = await kv.get('execution-count') || '0';
// Update state
await kv.put('last-execution', new Date(event.scheduledTime).toISOString());
await kv.put('execution-count', String(parseInt(count) + 1));
}
}