Scheduled tasks for v3

Now you can create scheduled tasks in Trigger.dev v3. If you haven't already you should sign up for the v3 waitlist.

At the end I'll reveal what schedule the CRON pattern in the image above, 0 17 * * 5, represents. You CRON nerds need closure.

In the video my scheduled task posts a GIF to Slack every minute.

Features

  • Define your task in code using schedules.task()
  • Attach many schedules (across environments) to each task in the dashboard.
  • Attach schedules using the SDK (you can do dynamic things like a schedule for each of your users using externalId)
  • List, view, edit, disable, re-enable, create and delete using the dashboard, SDK and REST API.
  • Use AI to help you create CRON patterns in the dashboard.

Full docs here: https://trigger.dev/docs/v3/tasks-scheduled

An example scheduled task

This is the scheduled task code from the video above.

/trigger/random-time-gif.ts

_33
import { WebClient } from "@slack/web-api";
_33
import { retry, schedules } from "@trigger.dev/sdk/v3";
_33
_33
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
_33
_33
//define the task using schedules.task()
_33
export const randomTimeGif = schedules.task({
_33
id: "random-time-gif",
_33
run: async (payload) => {
_33
//Get 25 GIFs related to "time" from the giphy API
_33
const randomGifResponse = await retry.fetch(
_33
`https://api.giphy.com/v1/gifs/search?api_key=${process.env.GIPHY_API_KEY}&q=time&limit=25&offset=0&rating=g&lang=en&bundle=messaging_non_clips`
_33
);
_33
const json = await randomGifResponse.json();
_33
_33
//pick a random GIF URL from the results
_33
const resultCount = json.data.length;
_33
const randomIndex = Math.floor(Math.random() * resultCount);
_33
const url = json.data[randomIndex]?.url;
_33
_33
if (!url) {
_33
throw new Error("No gif found");
_33
}
_33
_33
//post to a Slack channel
_33
const result = await slack.chat.postMessage({
_33
text: url,
_33
channel: process.env.SLACK_CHANNEL_ID!,
_33
});
_33
_33
return { success: true };
_33
},
_33
});

As shown in the video, you can attach a schedule to this task in the dashboard. This is great for most use cases.

Dynamic schedules (or multi-tenant)

Using the SDK you can do more advanced scheduling. For example, you could let your users define a schedule that they want to post GIFs to their own Slack workspace channel.

First take what we had before and modify the task slightly:

/trigger/scheduled-slack-gif.ts

_46
import { WebClient } from "@slack/web-api";
_46
import { retry, schedules } from "@trigger.dev/sdk/v3";
_46
import { db } from "@/db";
_46
_46
//define the task using schedules.task()
_46
export const scheduledSlackGifs = schedules.task({
_46
id: "scheduled-slack-gifs",
_46
run: async (payload) => {
_46
//we'll set the externalId to a row in our database when we create the schedule
_46
if (!payload.externalId) {
_46
throw new Error("externalId is required");
_46
}
_46
_46
//get the details of what the user wants
_46
const { slackToken, slackChannelId, gifSearchQuery } =
_46
await db.getGifSchedule(payload.externalId);
_46
_46
//Get 25 GIFs related to "time" from the giphy API
_46
const randomGifResponse = await retry.fetch(
_46
`https://api.giphy.com/v1/gifs/search?api_key=${
_46
process.env.GIPHY_API_KEY
_46
}&q=${encodeURIComponent(
_46
gifSearchQuery
_46
)}=25&offset=0&rating=g&lang=en&bundle=messaging_non_clips`
_46
);
_46
const json = await randomGifResponse.json();
_46
_46
//pick a random GIF URL from the results
_46
const resultCount = json.data.length;
_46
const randomIndex = Math.floor(Math.random() * resultCount);
_46
const url = json.data[randomIndex]?.url;
_46
_46
if (!url) {
_46
throw new Error("No gif found");
_46
}
_46
_46
//post to a Slack channel
_46
const slack = new WebClient(slackToken);
_46
const result = await slack.chat.postMessage({
_46
text: url,
_46
channel: slackChannelId,
_46
});
_46
_46
return { success: true };
_46
},
_46
});

Then in your backend code you need to register a schedule for this task. This is a Next.js server action but the only thing that matters is that it's on your server:


_33
"use server";
_33
_33
import { scheduledSlackGifs } from "@/trigger/scheduled-user-gifs";
_33
import { schedules } from "@trigger.dev/sdk/v3";
_33
import { db } from "@/db";
_33
_33
export async function registerSchedule(
_33
userId: string,
_33
cron: string,
_33
searchQuery: string
_33
) {
_33
try {
_33
//create a record for the GIF schedule for this user
_33
const row = await db.createGifSchedule(userId, searchQuery);
_33
_33
//create a new schedule for this GIF schedule
_33
const createdSchedule = await schedules.create({
_33
task: scheduledSlackGifs.id,
_33
cron,
_33
//the row id, so we can retrieve the row inside the run
_33
externalId: row.id,
_33
//don't allow multiple shedules for the same GIF schedule
_33
deduplicationKey: row.id,
_33
});
_33
_33
return { scheduleId: createdSchedule.id };
_33
} catch (error) {
_33
console.error(error);
_33
return {
_33
error: "something went wrong",
_33
};
_33
}
_33
}

The user has defined their own reminder frequency and what kind of GIFs they want.

Read the full docs for everything that scheduled tasks allow.

So the CRON pattern 0 17 * * 5 is every Friday at 5pm (UTC). I hope that helps you sleep at night.