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
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:
- Create an Application
- Create one or more Jobs to be run in the future
- 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.
The random token that's generated along with your Application will serve as your access token for the API.
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
}
}
}
Discussion