Skip to content

Job Definitions

Job Definitions are resources that wrap components to make them schedulable. They define what code should run when a job is executed.

What are Job Definitions?

A Job Definition is a simple resource that associates a unique ID with a component. It acts as a template for creating jobs:

Component (code) → Job Definition (wrapper) → Job (scheduled instance)

Why Job Definitions?

Job Definitions provide a layer of indirection that enables:

  • Reusability: One component can have multiple job definitions
  • Organization: Group related scheduled tasks
  • Permissions: Control who can schedule which components
  • Statistics: Track performance by job definition

Structure

A Job Definition consists of just two fields:

  • jobDefinitionId - Unique identifier for the definition
  • componentId - Which component to execute
uri: /resources/job-definitions/nightly-order-processing
spec:
componentId: process-orders.hreactor

Complete Example

Here’s how to create a scheduled background task:

  1. Create a component with the logic to execute:

    process-orders.hreactor
    import 'resources'
    param processingDate: text
    let orders = query orders(orderNumber)
    filter $'createdAt >= {processingDate}'
    orderBy 'createdAt asc'
    from orders select order =>
    sendEmail {
    to = order.customer.email
    subject = 'Processing Update'
    body = { html = '<p>Your order is being processed</p>' }
    dynamic = { orderId = order.id }
    }
  2. Create a job definition that wraps the component:

    uri: /resources/components/process-orders.hreactor
    spec:
    codeFile: process-orders.hreactor
    ---
    uri: /resources/job-definitions/nightly-order-processing
    spec:
    componentId: process-orders.hreactor
  3. Deploy the manifest:

    Terminal window
    h_ manage apply h_manifest.yaml
  4. Schedule a job using the definition:

    POST /resources/jobs
    {
    "jobDefinitionId": "nightly-order-processing",
    "parameters": {
    "processingDate": "2025-11-15T00:00:00Z"
    },
    "runAt": "2025-11-16T02:00:00Z"
    }

Now the component will run at 2 AM with the specified parameters.

One Component, Multiple Definitions

A single component can be wrapped by multiple job definitions for different purposes:

# Same component for different use cases
uri: /resources/job-definitions/hourly-sync
spec:
componentId: data-sync.hreactor
---
uri: /resources/job-definitions/daily-full-sync
spec:
componentId: data-sync.hreactor
---
uri: /resources/job-definitions/manual-sync
spec:
componentId: data-sync.hreactor

This allows:

  • Different scheduling patterns (hourly vs daily)
  • Different monitoring/statistics tracking
  • Different permission scopes
  • Same underlying logic

Managing Job Definitions

Job definitions are managed via the /resources/job-definitions API:

  • Create/Update: PUT /resources/job-definitions/{definitionId}
  • Get: GET /resources/job-definitions/{definitionId}
  • List: GET /resources/job-definitions
  • Delete: DELETE /resources/job-definitions/{definitionId}

See the API Reference for complete endpoint documentation.

Job Definition vs Job

Job Definition:

  • Template/configuration
  • Points to a component
  • Permanent resource
  • One per scheduled task type

Job:

  • Scheduled instance
  • References a job definition
  • Temporary (deleted after retention period)
  • Many instances per definition

Example:

  • Job Definition: nightly-order-processing (permanent)
  • Jobs: Individual executions (Nov 15 2AM, Nov 16 2AM, Nov 17 2AM, etc.)

Access Control

Managing job definitions requires permissions:

job-definitions:read # View job definitions
job-definitions:write # Create, update, delete job definitions

Scheduling jobs from a definition requires access to the underlying component - see Jobs documentation for details.

Statistics & Monitoring

Job statistics are tracked by jobDefinitionId, making it easy to monitor performance of specific scheduled tasks:

GET /resources/jobs/statistics?jobDefinitionId=nightly-order-processing

This allows you to track:

  • Success/failure rates for this specific task
  • Execution time trends
  • Queue depth for this definition

See Jobs documentation for complete statistics information.

Recurring Jobs Pattern

Hantera doesn’t have built-in cron scheduling. Instead, components can schedule themselves to create recurring jobs using relative time scheduling.

How It Works

A component schedules the next run at the end of its execution:

nightly-processor.hreactor
// Do the work
let orders = query orders(orderNumber)
filter 'status = pending'
let processResult = orders select order => processOrder(order)
// Schedule next run (24 hours from now)
from {
effect = 'scheduleJob'
definition = 'nightly-order-processing'
at = now addDays 1
parameters = {}
}

This creates a self-perpetuating chain: Job completes → Schedules next run → Next job executes → Schedules another run → …

Common Patterns

Hourly Processing

// At end of component
from {
effect = 'scheduleJob'
definition = 'hourly-sync'
at = now addHours 1
parameters = {}
}

Daily Processing

// At end of component
from {
effect = 'scheduleJob'
definition = 'daily-report'
at = now addDays 1
parameters = {}
}

Weekly Processing

// At end of component
from {
effect = 'scheduleJob'
definition = 'weekly-cleanup'
at = now addDays 7
parameters = {}
}

Custom Intervals

// Every 30 minutes
from {
effect = 'scheduleJob'
definition = 'frequent-sync'
at = now addMinutes 30
parameters = {}
}

Starting the Recurring Chain

Create the first job manually via API or rule to start the chain:

POST /resources/jobs
{
"jobDefinitionId": "nightly-order-processing",
"parameters": {},
"runAt": "2025-11-16T02:00:00Z"
}

After this first execution, the job schedules itself automatically.

Stopping Recurring Jobs

To stop a recurring job chain, cancel the next pending job:

DELETE /resources/jobs/{jobId}

When a job is cancelled, it never executes, so it never schedules the next job. The chain breaks automatically.

Alternative methods:

  • Update the component to remove self-scheduling logic (permanent change)
  • Delete the job definition (will fail any pending jobs)

Best Practices

Use Descriptive IDs

Choose clear job definition IDs that describe purpose and frequency:

  • hourly-inventory-sync, daily-report-generation, weekly-cleanup
  • job1, sync, task

Use Separate Definitions for Different Purposes

Create separate job definitions when you need distinct monitoring or different use cases:

# Same component, different purposes/monitoring
uri: /resources/job-definitions/sync-customers
spec:
componentId: sync.hreactor
---
uri: /resources/job-definitions/sync-products
spec:
componentId: sync.hreactor

This allows independent statistics tracking and clearer monitoring of each use case.

Check for Pending Jobs Before Deletion

Before deleting a job definition, verify no pending jobs exist:

GET /resources/jobs?jobDefinitionId=my-definition&status=pending

See Also

  • Components - Learn about creating components
  • Jobs - Scheduling and monitoring job executions
  • API Reference - Complete API documentation