loading...
Cover image for repeater.dev

repeater.dev

ajcwebdev profile image anthonyCampolo ・7 min read

Repeater.dev lets you execute tasks at a predefined time or on a recurring basis. Repeater is particularly good at calling an HTTP endpoint at a specific time and recording the result. It is built with RedwoodJS and was created by RedwoodJS core contributor Rob Cameron.

Potential jobs you can schedule include:

  • Send emails & notifications
  • Check for heartbeat
  • Image processing
  • Data transformations
  • PDF generation

Repeater is accessed through a GraphQL API:

  • https://api.repeater.dev/graphql

01-repeater-flow

If your client is capable of doing so you can issue an introspection query and receive a list of queries and mutations you can perform. repeaterdev-js is an official Javascript library that you can also use to skip worrying about your own GraphQL calls.

A typical workflow with Repeater may look something like:

  1. Create an Application
  2. Create one or more Jobs to be run in the future
  3. After a Job has run check the JobResult to confirm it completed successfully (or if it failed, why?)

Getting Started

First you'll need to create an account at repeater.dev. After logging in you can create an Application to serve as the owner of any Jobs you create.

02-new-application

The random token that's generated along with your Application will serve as your access token for the API.

03-applications

Authentication

Once you have the token from your Application you must include that in the header of every GraphQL call in the form of a Bearer token.

If you are using the repeaterdev-js library you create a new instance using your token as the first argument.

Authorization: Bearer 8edd2e13df6b840d23b47b0af177a62a
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 8edd2e13df6b840d23b47b0af177a62a" \
--data '{ "query": "{ jobs { name } }" }' \
https://api.repeater.dev/graphql
const repeater = new Repeater('8edd2e13df6b840d23b47b0af177a62a')

Applications

Top-level container for your Jobs

If you have two deployed webapps that need scheduled job processing, you would have an Application for each. Each would have a unique name. Creating an Application generates a unique token for authenticating each GraphQL API request.

You can only create Applications via the repeater.dev UI at this time. However you can request Application details as a field on other queries, like Jobs.

Field Type Description
name String Name of the Application
token String Used to authenticate GraphQL requests
createdAt ISO8601 Timestamp When this Application was created
updatedAt ISO8601 Timestamp When this Application was last updated
jobsCount Integer How many total Jobs belong to this Application
scheduledJobsCount Integer How many Jobs belonging to this Application are scheduled to be run in the future
query {
  jobs {
    name
    application {
      name
      token
    }
  }
}

Jobs

Main components of Repeater

A Job will contain a URL to hit somewhere on the internet and record the response as a JobResult. Jobs can be run once or on a recurring basis.

A Job must have a unique name (unique among its parent Application) which will act as its unique identifier for all query and mutation operations.

Field Type Description
name String Unique name for this Job
enabled Boolean Whether this Job is enabled and will run at the specified time(s)
endpoint String URL that will be requested
verb String HTTP verb to make the request with (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
headers String Javascript object containing any headers to set on the request
body String Any body to send along with the request
retryable Boolean Whether the Job should be retried if it fails
runAt ISO8601 Timestamp Timestamp of when the Job should run
runEvery ISO8601 Duration Duration of the recurring schedule for the Job
createdAt ISO8601 Timestamp Timestamp of when the Job was created
updatedAt ISO8601 Timestamp Timestamp of when the Job was last updated
lastRunAt ISO8601 Timestamp The last time a Runner for this Job was run
nextRunAt ISO8601 Timestamp Timestamp of when this Job will run next
successCount Integer Count of successful JobResults if any are present (HTTP response code < 400)
failureCount Integer Count of successful JobResults if any are present (HTTP response code >= 400)
application Application Details for the Application this Job is attached to
jobResults [JobResult] Details for the JobResults attached to this Job
runners [Runner] Any Runners that are scheduled to run

jobs

Query that returns all Jobs for the Application used to authenticate

query {
  jobs {
    name
    enabled
    endpoint
    verb
    headers
    body
    retryable
    runAt
    runEvery
    createdAt
    updatedAt
    lastRunAt
    nextRunAt
  }
}
const jobs = await repeater.jobs()

job

Query that returns details for the given Job name

A name argument is required (String) for the name of the Job.

query {
  job(name: "test-job-1") {
    name
    endpoint
  }
}
const job = await repeater.job('test-job-1')

createJob

Mutation that creates a Job

If a value is given for runEvery then the Job will run on that recurring schedule. Otherwise it will only run once at runAt.

Field Type Description
name String Unique name for this Job
enabled Boolean Whether this Job is enabled and will run at the specified time(s)
endpoint String URL to request
verb String HTTP verb to make the request with (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
headers String Any headers to send along with the request
body String Body content to send along with the request
retryable Boolean Whether the Job should be retried if it fails
runAt ISO8601 Timestamp Timestamp of when the Job should run
runEvery ISO8601 Duration Duration of the recurring schedule for the Job
mutation {
  createJob(
    name: "test-job-1"
    endpoint: "https://myapp.com/api/sendEmail"
    verb: "post"
    runAt: "2020-07-24T22:00:00Z"
    headers: "{\"Content-Type\":\"application/json\"}"
    body: "{\"foo\":\"bar\"}"
  ) {
    name
  }
}
const job = await repeater.enqueue({
  name: "test-job-1"
  endpoint: "https://myapp.com/api/sendEmail"
  verb: "post"
  runAt: new Date,
  headers: { 'Content-Type': 'application/json' }
  json: { foo: 'bar' }
})

updateJob

Mutation that updates a Job

Any existing Runners will be canceled and re-scheduled with the new runAt and runEvery arguments. Its arguments are the same as createJob attributes except that all attributes are optional except name.

You cannot change the name of a Job. Updating a Job first looks up the Job by the provided name argument and that's the one that is updated. Renaming a Job would be equivalent to deleting the existing one and creating a new one with the new name.

mutation {
  updateJob(
    name: "test-job-1"
    verb: "get"
  ) {
    name
  }
}
const job = await repeater.job('test-job-1')
await job.update({
  verb: "get"
})

createOrUpdateJob

Mutation that updates Job with provided name or creates Job with that name

If a Job is found with the provided name it is updated with the new values, otherwise a Job is created with that name. Its arguments are the same as createJob.

mutation {
  createOrUpdateJob(
    name: "test-job-1"
    endpoint: "https://myapp.com/api/sendEmail"
    verb: "post"
    runAt: "2020-07-24T22:00:00Z"
    headers: "{\"Content-Type\": \"application/json\"}"
    body: "{\"foo\":\"bar\"}"
  ) {
    name
  }
}
const job = await repeater.enqueueOrUpdate({
  name: "test-job-1"
  endpoint: "https://myapp.com/api/sendEmail"
  verb: "post"
  runAt: new Date,
  headers: { 'Content-Type': 'application/json' }
  json: { foo: 'bar' }
})

deleteJob

Mutation that deletes a Job and any pending Runners

This will also remove any JobResult history. A name argument is required (String) for the name of the Job.

mutation {
  deleteJob(name: "test-job-1") {
    name
  }
}
const job = await repeater.job('test-job-1')
await job.delete()

JobResults

The record of what happened when a Job was run

JobResults have their own endpoint but can also be returned as a sub-type of Jobs themselves.

Field Type Description
status Integer HTTP status code in the response
headers String Any headers returned with the response
body String Any body in the response
runAt ISO8601 Timestamp Timestamp of when the Job was run which created this JobResult
run Integer A counter that is incremented every time the parent Job was run
duration Integer The number of milliseconds it took to receive the response
createdAt ISO8601 Timestamp Timestamp of when the JobResult was created
updatedAt ISO8601 Timestamp Timestamp of when the JobResult was last updated
job Job Details for the Job this JobResult is attached to

jobResults

Query that if given the jobName argument returns all JobResults for that Job

Otherwise it returns all JobResults for the Application used to authenticate. A jobName argument is optional (String) for the name of a parent Job.

query {
  jobResults(jobName: "test-job-1") {
    status
    headers
    body
    runAt
    run
    duration
    createdAt
    updatedAt
  }
}
const job = await repeater.job('test-job-1')
const results = await job.results()

Runners

Represents the actual processes that execute your Job

You will only see Runner records for Jobs that are actively running or still scheduled to be run in the future. There is no stand-alone Runners query. You can only return Runners as a sub-type of a Jobs query.

Field Type Description
priority Integer Number ranging from 0-100 that represents the priority for this Runner, lower numbers have higher priority
queue String The named queue for this Runner
attempts Integer If a Runner fails this field is incremented with every retry
recurring Boolean Denotes whether this Runner is scheduled to recur
running Boolean Denotes whether the Runner is actively executing
runAt ISO8601 Timestamp Timestamp of when the Runner is scheduled to execute
failedAt ISO8601 Timestamp A timestamp of when the Runner last failed (if the endpoint returned a status code of >= 400 and the Job is marked as retryable)
createdAt ISO8601 Timestamp Timestamp of when the Runner was created
updatedAt ISO8601 Timestamp Timestamp of when the Runner was last updated
job Job Detail for the attached Job
query {
  jobs {
    name
    runners {
      priority
      queue
      attempts
      recurring
      running
      runAt
      failedAt
      createdAt
      updatedAt
    }
  }
}

Posted on by:

Discussion

pic
Editor guide