Getting started

To start using Qron, it is strongly encouraged to begin with a tutorial, which will guide through the setup process for your chosen framework and will provide a basic but foundational grasp on how to use the library.

Make sure to read the quick architecture overview before start coding.

Creating a client

To create a client, you will need to import the createClient function from the @qron-run/sveltekit package. The client can be completely configured via env variables, you can directly pass properties to the config object to override them.

Supported env variables are:

  • QRON_API_KEY - Your Qron API key
  • QRON_API_URL - The Qron API URL
  • NODE_ENV - This will determine if the sdk is running in production mode

The config object supports the following properties:

Property Description Prod Default Dev default Required
token Your Qron API key undefined undefined true
publicUrl The public url of your application undefined undefined true
url The Qron API URL https://api.qron.io http://localhost:3000 false
prod Whether the sdk is running in production mode equality check with NODE_ENV false false

You can instantiate a new client by:

import { createClient } from '@qron-run/sveltekit'

export const { createQueue, createCron, sdk } = createClient()

Creating a queue

To create a queue, you will need to use the createQueue function created when instantiating a client.

export const helloq = createQueue('helloq', async (event) => {
  // ...
})

Each queue has a name and a handler function. The handler function will be invoked when a new job needs to be processed.

The event argument contains the following properties:

  • retry - Retry<T> class used to retry the job, the job will turn into READY state
  • commit - Commit<T> class used to commit the job, once called the job will be in SUCCESS state
  • fail - Fail<T> class used to fail the job, once called the job will be in FAILURE state
  • pause - Pause<T> class used to momentarily pause the job, once called the job will be in PAUSED state
  • state - stateful object that will be persisted between retries. This can hold specific information about the job

Creating a cron

Creating a cron is identical to creating a queue, you will need to use the createCron function created when instantiating a client.

export const dailyreminder = createCron('dailyreminder', async (event) => {
  // ...
})

Each cron has a name and a handler function. The handler function will be invoked when the scheduled job needs to be processed. The event argument contains the same properties as the queue handler.

Mount the handlers

In order to be able to process jobs, you need to mount the qron handlers in your sveltekit application. Create a file named src/routes/api/queues/[name]/+server.ts and add the following code:

import { counterq, helloq } from '$lib/your/queues/location'
import { createHandler } from '@qron-run/sveltekit'

export const POST = createHandler(
  helloq,
  counterq,
)

To make sure your queues and cron jobs are reachable via POST method at the path /api/queues/:name.

Type safe state

It is possible to pass a zod schema when creating a queue or cron handler so to make sure that every time a state is persisted across runs or at scheduling time, it is always compliant to the schema. This is also handly as it speeds up development as you will enjoy autocompletion on the state object.

import { z } from 'zod'

export const helloq = createQueue('helloq', async ({ state }) => {
  console.log(`received greeting from ${state.name} ⏰`)
  // ...
}, z.object({
  name: z.string()
}))

NOTE: state will be serialized to JSON before being persisted, so make sure that everything you put in the state is JSON serializable.

Commit

It is possible to commit a job and mark it as successfully processed by calling the commit function passed to the handler. This will put the job in a terminal state, meaning it will not be executed anymore.

export const helloq = createQueue('helloq', async ({ state, commit }) => {
  console.log('⏰ received greeting from', state.name)

  return commit()
}, z.object({
  name: z.string()
}))

Fail

It is possible to fail a job and mark it as failed by calling the fail function passed to the handler. This will put the job in a terminal state, meaning it will not be executed anymore.

The fail function accepts an optional argument that will be persisted as the state of the job. If no argument is passed, the state will not be updated.

export const helloq = createQueue('helloq', async ({ state, commit, fail }) => {
  try {
    // execute greet function
    return commit()
  } catch (e) {
    console.error(e)
    return fail({
      ...state,
      error: e.message
    })
  }
}, z.object({
  name: z.string(),
  error: z.string()
}))

This example shows how to fail a jop and persist the error message. It is possible to inspect the state of a job in the Qron dashboard.

Pause

It is possible to pause a job by calling the pause function passed to the handler. This will put the job in PAUSED state. The job will not be picked up until it is resumed either through the sdk or manually via the dashboard.

export const helloq = createQueue('helloq', async ({ state, pause }) => {
  console.log('⏰ received greeting from', state.name)

  return pause()
}, z.object({
  name: z.string()
}))

Retry

It is possible to retry a job by calling the retry function passed to the handler. This will put the job in READY state and it will be picked up after the specified delay.

export const helloq = createQueue('helloq', async ({ state, retry }) => {
  console.log('⏰ received greeting from', state.name)

  return retry().afterDays(1)
}, z.object({
  name: z.string(),
  count: z.number()
}))

It is possible to invoke the retry optionally passing the next state to be persisted.

export const helloq = createQueue('helloq', async ({ state, retry }) => {
  console.log('⏰ received greeting from', state.name, state.count, 'times')

  return retry({
    ...state,
    count: state.count + 1
  }).afterDays(1)
}, z.object({
  name: z.string(),
  count: z.number()
}))

In this example we are incrementing the count property of the state object. Which will increase by 1 every time the job is retried. If no state is passed to the retry function, the state will not change. In this case the job will be retried after 1 day, the retry function supports many scheduling functions:

  • asap() - retry the job as soon as possible
  • afterInterval(interval: number, unit: TimeUnit) - retry the job after the specified interval (e.g. 1 day, 2 hours, 3 minutes)
  • afterMinutes(interval: number) - retry the job after the specified number of minutes
  • afterHours(interval: number) - retry the job after the specified number of hours
  • afterDays(interval: number) - retry the job after the specified number of days
  • afterWeeks(interval: number) - retry the job after the specified number of weeks
  • afterMonths(interval: number) - retry the job after the specified number of months
  • afterYears(interval: number) - retry the job after the specified number of years
  • afterTime(time: Date) - retry the job at the specified time

Where TimeUnit is one of the following:

  • mins - minutes
  • hours - hours
  • days - days
  • weeks - weeks
  • months - months
  • years - years

A full example of a greeting queue that continuously retries up to a specified count of times is the following:

import { z } from 'zod'

export const helloq = createQueue('helloq', async ({ state, retry, commit }) => {
  console.log('⏰ received greeting from', state.name, state.count, 'times')

  if (state.count < 3) {
    return retry({
      ...state,
      count: state.count + 1
    }).afterDays(1)
  }

  console.log('🎉 success')
  return commit()
}, z.object({
  name: z.string(),
  count: z.number()
}))

Scheduling a job

It is possible to schedule a job to be executed at a specific time or after a certain delay by calling the function created when creating a queue.

export const helloq = createQueue('helloq', /* ... */)

// In your handler ..
// this job will be executed 30 minutes after 1st Jan 2024
await helloq
  .afterMinutes(30)
  .startsAt(new Date(2024, 1, 1))
  .schedule({ name: 'world' })

The job scheduler class Job<T> supports many methods that can be chained to configure the job execution. When the job is configured, the schedule() method needs to be called in order to persist the job. The schedule(state?: T) method optionally receives state as argument which will be persisted by Qron and passed to the job handler.

Supported methods are:

  • startsAt(date: Date) - set the start time of the job after the specified date
  • startsAfter(interval: number, unit: TimeUnit) - set the start time of the job after the specified interval
  • after(interval: number, unit: TimeUnit) - set the cron expression of the job to @after interval unit
  • at(date: Date) - set the cron expression of the job to @at date. Job will be executed at or right after the specified date
  • asap() - The job will be executed as soon as possible
  • afterMinutes(interval: number) - set the cron expression of the job to @after interval mins
  • afterHours(interval: number) - set the cron expression of the job to @after interval hours
  • afterDays(interval: number) - set the cron expression of the job to @after interval days
  • afterWeeks(interval: number) - set the cron expression of the job to @after interval weeks
  • afterMonths(interval: number) - set the cron expression of the job to @after interval months
  • afterYears(interval: number) - set the cron expression of the job to @after interval years

NOTE: It is only possible to schedule jobs server side. This is because the qron sdk requires authentication. And, given that background jobs may process sensitive information, to make sure you are in control of what is published for execution

Scheduling a recurring job

It is possible to schedule a recurring job by calling the function created when creating a cron.

export const reminder = createCron('reminder', async ({ state, commit }) => {
  await sendReminderEmail(state.userId, `Remember to drink water!`)

  return commit()
}, z.object({
  userId: z.string(),
}))


// In your handler ..
await reminder
  .everyWeeks(1)
  .startsAt(new Date(2024, 1, 1))
  .schedule({
    userId: '123',
  })
// this job will be executed every 1 week after 1st Jan 2024

It is important to return the commit() class from the job handler in order to signal to Qron that the execution were successful.

Supported methods are:

  • startsAt(date: Date) - set the start time of the cron job after the specified date
  • startsAfter(interval: number, unit: TimeUnit) - set the start time of the cron job after the specified interval
  • every(interval: number, unit: TimeUnit) - set the cron expression of the job to @every interval unit
  • everyMinutes(interval: number) - set the cron expression of the job to @every interval mins
  • everyHours(interval: number) - set the cron expression of the job to @every interval hours
  • everyDays(interval: number) - set the cron expression of the job to @every interval days
  • everyWeeks(interval: number) - set the cron expression of the job to @every interval weeks
  • everyMonths(interval: number) - set the cron expression of the job to @every interval months
  • everyYears(interval: number) - set the cron expression of the job to @every interval years

Cloud authentication

In order to authenticate with Qron you need to create an access token at the dedicated page. Then set the QRON_TOKEN variable or provide the token option to the createClient function.

NOTE: When developing locally, the cloud version cannot be used as it requires a serverless function that is exposed to the internet. Setup a local development environment instead.

If you have any further questions or issues, join the Discord community.