Adding docs back into this repo

This commit is contained in:
Eric Allam
2023-08-02 15:09:12 +01:00
parent aa9fe7d408
commit 342ab0d471
158 changed files with 7884 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.DS_Store
+14
View File
@@ -0,0 +1,14 @@
# Development
## Install and initial setup
`npm install`
## Running the app
`npm run dev`
## View the app locally
It runs locally here:
`http://localhost:3050`
+18
View File
@@ -0,0 +1,18 @@
```typescript Wait example
new Job(client, {
id: "delay-job",
name: "Delay Job",
version: "0.0.1",
trigger: eventTrigger({
name: "example.event",
}),
run: async (payload, io, ctx) => {
await io.logger.info("Hi");
// the second parameter is the number of seconds to wait
await io.wait("wait 30 days", 30 * 24 * 60 * 60);
await io.logger.info("Sorry for the slow reply!");
},
});
```
@@ -0,0 +1,15 @@
```typescript
new Job(client, {
//... other options
integrations: {
slack,
gh: github,
},
run: async (payload, io, ctx) => {
//slack is available on io.slack
io.slack.postMessage(...);
//github is available on io.gh
io.gh.addIssueLabels(...);
}
});
```
@@ -0,0 +1,3 @@
## Getting started
If you have not yet set up Trigger.dev in your Next.js project, go to the [quick start guide](/documentation/quickstart).
+6
View File
@@ -0,0 +1,6 @@
![List of integrations](/images/integrations-list.png)
The "Integrations" page shows
1. Some of the APIs that you can use with Trigger.dev (you can use any API). Selecting one of these will give you instructions on how to use it.
2. Your connected integrations. Clicking these will give you more details on that connection.
+6
View File
@@ -0,0 +1,6 @@
<Warning>
Scheduled Triggers **do not** trigger Jobs in the DEV
[Environment](/documentation/concepts/environments-endpoints). When you're
working locally you should use [the Test
feature](/documentation/guides/testing-jobs) to trigger any scheduled Jobs.
</Warning>
+4
View File
@@ -0,0 +1,4 @@
There are two way to send an event that will trigger `eventTrigger()`:
1. Use [client.sendEvent()](/sdk/triggerclient/instancemethods/sendevent) from anywhere in your codebase.
2. Use [io.sendEvent()](/sdk/io/sendevent) from inside a Job's `run()` function.
+52
View File
@@ -0,0 +1,52 @@
<ParamField body="event" type="object" required>
<Expandable title="properties" defaultOpen>
<ParamField body="name" type="string" required>
The `name` property must exactly match any subscriptions you want to
trigger.
</ParamField>
<ParamField body="payload" type="any">
The `payload` property will be sent to any matching Jobs and will appear
as the `payload` param of the `run()` function. You can leave this
parameter out if you just want to trigger a Job without any input data.
</ParamField>
<ParamField body="context" type="any">
The optional `context` property will be sent to any matching Jobs and will
be passed through as the `context.event.context` param of the `run()`
function. This is optional but can be useful if you want to pass through
some additional context to the Job.
</ParamField>
<ParamField body="id" type="string">
The `id` property uniquely identify this particular event. If unset it
will be set automatically using `ulid`.
</ParamField>
<ParamField body="timestamp" type="Date">
This is optional, it defaults to the current timestamp. Usually you would
only set this if you have a timestamp that you wish to pass through, e.g.
you receive a timestamp from a service and you want the same timestamp to
be used in your Job.
</ParamField>
<ParamField body="source" type="string">
This is optional, it defaults to "trigger.dev". It can be useful to set
this as you can filter events using this in the `eventTrigger()`.
</ParamField>
</Expandable>
</ParamField>
<ParamField body="options" type="object">
<Expandable title="properties" defaultOpen>
<ParamField body="deliverAt" type="Date">
An optional Date when you want the event to Trigger Jobs. The event will
be sent to the platform immediately but won't be acted upon until the
specified time.
</ParamField>
<ParamField body="deliverAfter" type="number">
An optional number of seconds you want to wait for the event to Trigger
any relevant Jobs. The event will be sent to the platform immediately but
won't be acted upon until the specified time.
</ParamField>
<ParamField body="accountId" type="string">
This optional param will be used by the Trigger.dev Connect feature, which
is coming soon.
</ParamField>
</Expandable>
</ParamField>
+29
View File
@@ -0,0 +1,29 @@
<ResponseField type="object">
<Expandable title="properties" defaultOpen>
<ResponseField name="id" type="string" required>
The `id` of the event that was sent.
</ResponseField>
<ResponseField name="name" type="string" required>
The `name` of the event that was sent.
</ResponseField>
<ResponseField name="payload" type="any" required>
The `payload` of the event that was sent
</ResponseField>
<ResponseField name="timestamp" type="Date" required>
The `timestamp` of the event that was sent
</ResponseField>
<ResponseField name="context" type="any">
The `context` of the event that was sent. Is `undefined` if no context was
set when sending the event.
</ResponseField>
<ResponseField name="deliverAt" type="Date">
The timestamp when the event will be delivered to any matching Jobs. Is
`undefined` if `deliverAt` or `deliverAfter` wasn't set when sending the
event.
</ResponseField>
<ResponseField name="deliveredAt" type="Date">
The timestamp when the event was delivered. Is `undefined` if `deliverAt`
or `deliverAfter` were set when sending the event.
</ResponseField>
</Expandable>
</ResponseField>
+4
View File
@@ -0,0 +1,4 @@
<ResponseField name="key" type="string" required>
Should be a stable and unique key inside the `run()`. See
[resumability](/documentation/concepts/resumability) for more information.
</ResponseField>
+15
View File
@@ -0,0 +1,15 @@
---
title: "Changelog"
description: "We release features and fixes weekly! See some of the changes we have made."
---
## June 28 2023 Release
The first release of Trigger.dev v2, a major overhaul.
- Works well on serverless and long-running servers
- Adaptors for Next.js
- Simpler architecture utilizing Postgres
- Integrations are now just Node.js packages
- New UI
- Easy self-hosting
@@ -0,0 +1,31 @@
---
title: "Client & Adaptors"
description: "The Client is how you interact with the API, through an Adaptor."
---
## Client
A Client is used to connect to a specific [Project](/documentation/concepts/projects) by using an [API Key](/documentation/concepts/environments-apikeys).
Clients are created using the `TriggerClient` class.
```ts
export const client = new TriggerClient({
id: "my-webapp",
apiKey: process.env.TRIGGER_API_KEY!,
});
```
View the [Client API Reference](//sdk/triggerclient) for more information.
## Adaptors
Adaptors allows Clients to receive data from the Trigger API. They do this by creating an API endpoint that data can be received at and transforming data into the expected format.
Each platform has one or more adaptors, see the guides below:
| Platform | Adaptor |
| ------------------------------------------------- | -------------------- |
| [Next.js](/documentation/guides/platforms/nextjs) | `createPagesRoute()` |
| [Next.js](/documentation/guides/platforms/nextjs) | `createAppRoute()` |
| Express | Coming soon |
+23
View File
@@ -0,0 +1,23 @@
---
title: Trigger.dev Connect
description: "Authenticate your users with an Integration using Trigger.dev Connect."
---
<Note>Trigger.dev Connect will be released soon, during the beta period</Note>
## What is it?
It allows you to easily add Integrations for your users, such as:
- Add contacts to their Hubspot.
- Sync data to their Airtable.
- Post videos to their YouTube.
- Add issues to their Linear.
## How does it work?
1. Create an [Integration in the Dashboard](/documentation/guides/integrations).
2. Use our provided React components in your web app to authenticate your users.
3. Trigger a Job as one of your users.
4. Inside the Run function the relevant Integrations will be authenticated with your user's credentials.
5. Profit.
+12
View File
@@ -0,0 +1,12 @@
---
title: Delays
description: "Delays can be used to wait for a certain amount of time before continuing a Run."
---
[Runs](/documentation/concepts/runs) can be paused using delays. They can be very long.
Delays works even if you're on a serverless platform with timeouts, or if your server goes down. They utilize [resumability](/documentation/concepts/resumability) to ensure that the Run can be resumed after the delay.
## Usage
<Snippet file="delay-example.mdx" />
@@ -0,0 +1,40 @@
---
title: "Environments & Endpoints"
description: "Environments and Endpoints are used to connect your server to the Trigger.dev platform."
---
![Environments and Endpoints](/images/environments.png)
When you login to your Project there's a section called "Environments & API Keys".
## Environments
Each environment has an API Key associated with it. This API Key is used to authenticate your Jobs with the Trigger.dev platform.
The API Key you use for your [Client](/documentation/concepts/client-adaptors) is how we know which environment to run your code against:
```ts
export const client = new TriggerClient({
id: "nextjs-example",
//this environment variable should be set to your DEV API Key locally,
//and your PROD API Key in production
apiKey: process.env.TRIGGER_API_KEY!,
});
```
### Development
The `DEV` environment should only be used for local development. It's where you can test your Jobs before deploying them to servers.
<Snippet file="scheduled-dev-warning.mdx" />
### Production
The `PROD` environment is where your Jobs will run in production. It's where you can run your Jobs against real data.
## Endpoints
An Endpoint is a URL on your server that Trigger.dev can connect to. This URL is used to register Jobs, start them and orchestrate runs and retries.
`DEV` has multiple endpoints associated with it one for each team member. This allows each team member to run their own Jobs, without interfering with each other.
All other environments have just a single endpoint (with a single URL) associated with them.
@@ -0,0 +1,112 @@
---
title: Integrations
description: "Integrations make it easy to use APIs in your Jobs"
---
<Note>
You can use any API in your Jobs by using existing Node.js SDKs or HTTP
requests. Integrations just make it much easier especially when you want to
use OAuth. And you get great logging.
</Note>
An Integration is a package you install that makes it easy to work with a specific API. They:
- Create a connection to the API (using OAuth, API keys or tokens)
- Provide a set of [Tasks](/documentation/concepts/tasks) to work with the API
- Provide a set of [Triggers](/documentation/concepts/triggers) to listen for events from the API
- Allow you to use the **full underlying authenticated SDK client** directly
## An example
This job sends Slack messages when a new GitHub issue is opened on the `triggerdotdev/trigger.dev` repo.
```ts
import { Github } from "@trigger.dev/github";
import { Slack } from "@trigger.dev/slack";
//1. create GitHub client using a token
const github = new Github({
id: "github",
token: process.env.GITHUB_TOKEN!,
});
//2. create Slack client using OAuth
const slack = new Slack({
id: "slack",
});
new Job(client, {
id: "alert-on-new-github-issues",
name: "Alert on new GitHub issues",
version: "0.1.1",
integrations: {
//3. include the slack integration
slack,
},
//4. use the github integration to listen for new issues
trigger: github.triggers.repo({
event: events.onIssueOpened,
owner: "triggerdotdev",
repo: "trigger.dev",
}),
run: async (payload, io, ctx) => {
//5. send a message to slack
const response = await io.slack.postMessage("Slack alert", {
text: `New Issue opened: ${payload.issue.html_url}`,
channel: "C04GWBTDQ7W",
});
return response;
},
});
```
There are some things worth highlighting here:
1. Integration clients can use API keys or tokens.
2. Integration clients can use OAuth.
3. Integrations are added to a Job by adding them to the `integrations`.
4. The Job Trigger can use an Integration to listen for events from the API.
5. You can use the Integration to make API calls inside the `run` function.
## Authentication
### API Keys and Tokens
You the value in when creating your Integration client. They are never sent from your server by the Trigger.dev service, they are local to your servers. We recommend you use a secure method of storing these values and passing them to your code, like environment variables.
### OAuth
You can use the [Integrations Dashboard](/documentation/guides/using-integrations) to setup OAuth for an Integration. We make it easy to use OAuth by dealing with the OAuth flow, token refreshing and storage for you.
You can use OAuth to authenticate your internal team with an Integration or to allow your users to authenticate with an Integration we call user authentication [Trigger.dev Connect](/documentation/guides/connect).
## References
<CardGroup>
<Card
title="Integrations Dashboard"
icon="sidebar"
href="documentation/guides/integrations"
>
The Integrations Dashboard allows you to manage your Integrations and setup
OAuth.
</Card>
<Card
title="Trigger.dev Connect"
icon="user-plus"
href="/documentation/concepts/connect"
>
Authenticate your users with an Integration using Trigger.dev Connect.
</Card>
<Card title="View Integrations" icon="grid-2" href="/integrations">
Trigger.dev integrates with a wide range of services.
</Card>
<Card
title="Create an Integration"
icon="square-plus"
href="/integrations/create"
>
Create an Integration for your own use or as a public package.
</Card>
</CardGroup>
+61
View File
@@ -0,0 +1,61 @@
---
title: "Jobs"
description: "When this happens, do this."
---
> A Job defines what event triggers it and what should happen when it is run.
For example, you can create a Job that will send an email to a user when they sign up for your app.
## Anatomy of a Job
A Job is made up of a few things:
1. Metadata
2. A Trigger (what event should cause this job to run)
3. The Run function (what should happen when this job runs)
```ts
//Job definition uses the client
new Job(client, {
// 1. Metadata
id: "event-1",
name: "Run when the foo.bar event happens",
version: "0.0.1",
// 2. Trigger
trigger: eventTrigger({
name: "foo.bar",
}),
// 3. Run function
run: async (payload, io, ctx) => {
// do something
},
});
```
## Jobs, Triggers, Runs and Tasks
A more complicated Job
> When a GitHub issue is modified: If the issue has been labelled "critical" send a Slack message and sync the issue to Linear.
This can be visualized like this:
![Job](/images/job-concept.png)
Events [trigger](/documentation/concepts/triggers) Jobs. Jobs generate a [Run](/documentation/concepts/runs) for every event. A Run is a single execution of a Job. A Run can have multiple [Tasks](/documentation/concepts/tasks) which are the individual steps of a Run.
## References
<CardGroup>
<Card title="Job SDK reference" icon="wrench" href="/sdk/job">
Detailed SDK reference for Jobs.
</Card>
<Card
title="Managing Jobs Dashboard"
icon="globe"
href="/documentation/guides/managing-jobs"
>
Viewing and managing your Jobs in the Dashboard.
</Card>
</CardGroup>
@@ -0,0 +1,31 @@
---
title: "Limitations"
---
There are a few limitations that are important to understand.
In the current beta:
- Runs on localhost are limited to 5 minutes.
- On long-running servers (not serverless) Runs can be retried erroneously.
- Compute intensive jobs are not well supported.
## Runs on localhost are limited to 5 minutes
When developing locally the [CLI dev command](/documentation/guides/cli#dev-command) uses [ngrok](https://ngrok.com/) so messages can be sent to your machine.
Ngrok has a timeout of 5 minutes on a Request/Response cycle. so, if a localhost Run takes longer than 5 minutes to complete, the Run will fail.
This limitation will be removed in the future by adding an alternative run strategy that works well on localhost and long-running servers. This won't use the request/response cycle.
## On long-running servers (not serverless) Runs can be retried erroneously
Currently the only way that Runs are performed is by a Request/Response cycle when `run` is called on a Job. This is optimized for serverless functions (where you have to use a Request/Response cycle), but not for long-running servers.
This limitation will be removed in the future by adding an alternative mode so Jobs works well on localhost and long-running servers. This won't use the request/response cycle.
## Compute intensive jobs are not well supported
Currently the only way that Runs are performed is inside a Request/Response cycle when `run` is called on a Job. This is not a good way to perform compute intensive jobs.
In the future we will add good support for compute intensive jobs.
+10
View File
@@ -0,0 +1,10 @@
---
title: "Projects"
description: "Projects are a group of [Jobs](/documentation/concepts/jobs-runs-tasks) with [Environments](/documentation/concepts/environments-apikeys)"
---
User's are members of one or more Organizations. Each Organization can have many Projects.
Jobs are scoped to a specific Project. This means they can only be run in the context of a Project. Jobs are associated with a Project by using a Project API key.
Currently all members of an Organization have access to all Projects. More granular access control will be added in the future.
@@ -0,0 +1,58 @@
---
title: Resumability
description: "Runs are resumable by returning Task stored data"
---
[Runs](/documentation/concepts/runs) can exceed the maximum timeout on serverless platforms and can survive server restarts.
## How does this work?
1. When a Run is created, it is given a unique ID. This ID is used to identify the Run.
2. [Tasks](/documentation/concepts/tasks) have a `key` which is a string and is the first parameter. This should be stable and unique inside that `run` function.
3. When a Task is completed, its output is stored.
4. If a Run exceeds the timeout, or your server restarts, the Run will be "replayed".
5. The second+ time it is run, Tasks that have already successfully completed will immediately return their first output. The code inside them won't re-run.
## How to use keys
Like we mentioned above, we use Task keys to determine which Tasks have already been executed. They are defined by you inside your `run` function, for example when you call `io.slack.postMessage`:
```ts
await io.slack.postMessage("⭐️ New Star", {
channel: "C04GWUTDC3W",
text: `@${starredBy} just starred ${repoName}!`,
});
```
In this example, the key is the string `"⭐️ New Star"`. This means that if the Job is interrupted and then resumed, the `slack.postMessage` Task will be skipped because it has already been executed.
If you make multiple calls to `slack.postMessage`, you should use different keys for each call. For example:
```ts
await io.slack.postMessage("⭐️ New Star", {
channel: "C04GWUTDC3W",
text: `@${starredBy} just starred ${repoName}!`,
});
await io.slack.postMessage("🚨 Critical Issue", {
channel: "C04GWUTDC3W",
text: `@${assignee} just opened a critical issue in ${repoName}!`,
});
```
If you are calling a Task multiple times with the same key, it will only be executed once. For example, if you call `slack.postMessage` with the key `"⭐️ New Star"` twice, it will only be executed once.
## How to use keys with loops
If you are using a loop, you should use the loop index as the key. For example:
```ts
for (let i = 0; i < 10; i++) {
await ctx.waitFor(`Wait ${i}`, { seconds: 30 });
await slack.postMessage(`⭐️ New Star ${i}`, {
channelName: "github-stars",
text: `@${starredBy} just starred ${repoName}!`,
});
}
```
+80
View File
@@ -0,0 +1,80 @@
---
title: "Runs"
description: "When a [Job](/documentation/concepts/jobs) is [Triggered](/documentation/concepts/triggers), the Run function is called."
---
> Everytime a Job is triggered, a Run is created with a payload of data.
## Anatomy of a Run
A Run is a record of the execution of a Job. It is created from `run()` function of a Job.
```ts
new Job(client, {
id: "event-1",
name: "Run when the foo.bar event happens",
version: "0.0.1",
trigger: eventTrigger({
name: "foo.bar",
schema: z.object({
url: z.string(),
}),
}),
// 1. Run function with params
run: async (payload, io, ctx) => {
// 2. Regular code and Tasks
},
});
```
1. The `run()` function is called with some useful parameters. More on that in a second.
2. Inside the run function you can write regular code and use [Tasks](/documentation/concepts/tasks).
## Resumability
Runs can exceed the maximum timeout on serverless platforms. If a Run exceeds this limit, it will be re-run. When it is re-run, any completed Tasks return their original output and they aren't re-run. Read more about [Resumability](/documentation/concepts/resumability).
## Run function parameters
### payload
The payload is the data that triggered the Job. It is the same data that was sent to the [Trigger](/documentation/concepts/triggers) that triggered the Job.
- For [Webhooks](/documentation/concepts/triggers/webhooks) the payload is the data from the webhook.
- For [Events](/documentation/concepts/triggers/events) the payload is the data from the event, in the example above that's `{ url: "https://..." }`.
- For [Scheduled](/documentation/concepts/triggers/scheduled) the payload is an object with the timestamp and the last timestamp (previous run).
### io
The `io` object gives you access to [Integrations](/documentation/concepts/integrations) and other useful functions. [View the full reference](/sdk/io) for `io`.
A few things you can do with `io`:
- Use [Integrations](/documentation/concepts/integrations).
- Add [delays](/documentation/concepts/delays) (that can be longer than your server timeout).
- Log messages to the [Run log](/documentation/guides/viewing-runs).
- Perform [background fetch requests](/documentation/sdk/io) (that can be longer than your server timeout).
- [Send events](/documentation/concepts/triggers/events) to Trigger other Jobs.
- Create a [Task](/documentation/concepts/tasks) manually by wrapping code in `io.runTask`.
### context
The `context` object gives you access to information about the current Run, Job, Environment, Organization and Event. [View the full reference](/sdk/context) for `context`.
## References
<CardGroup cols={2}>
<Card
title="Viewing Runs Dashboard"
icon="globe"
href="/documentation/guides/viewing-runs"
>
View all Runs for a Job, all the way down to individual Tasks.
</Card>
<Card title="`io` SDK Reference" icon="wrench" href="/sdk/io">
The `io` object gives you access to Integrations and other useful functions.
</Card>
<Card title="`context` SDK Reference" icon="wrench" href="/sdk/context">
The `context` object gives you access to the current Run's context.
</Card>
</CardGroup>
+103
View File
@@ -0,0 +1,103 @@
---
title: "Tasks"
description: "Tasks are individual building blocks of a Run."
---
> A Task is a resumable unit of a Run that can be retried, resumed and is logged.
## Tasks vs regular code
In the `run()` function you can use regular code and you can use Tasks.
```ts
new Job(client, {
id: "new-user",
name: "Run when a new user signs up",
version: "0.0.1",
trigger: eventTrigger({
name: "new.user",
schema: z.object({
userId: z.string(),
}),
}),
integrations: {
resend,
},
run: async (payload, io, ctx) => {
// regular code, not a Task
// the inputs/outputs of this function are not sent to the Trigger.dev platform
const user = await prisma.user.findUnique({
where: { id: payload.userId },
select: { email: true, name: true },
});
if (!user) throw new Error(`User not found: ${payload.userId}`);
// Integration functions are Tasks
await io.resend.sendEmail("Welcome email", {
to: user.email,
from: "jane@acme.inc",
subject: "Welcome!",
html: welcomeEmail(user.name),
});
// built-in io functions are Tasks
await io.wait("wait", 60 * 60 * 3); // wait for 3 hours
// You can wrap your own code in a Task, for retrying, resumability and logging
const response = await io.runTask(
"my-task",
{ name: "My Task" },
async () => {
return await longRunningCode(payload.userId);
}
);
return response;
},
});
```
## The benefits of Tasks
Tasks are a powerful concept that gives you a lot of benefits:
- **Resumability** Runs can exceed the maximum timeout on serverless platforms. If a Run exceeds this limit, it will be re-run. When it is re-run, any completed Tasks return their original output and they aren't re-run. Read more about [Resumability](/documentation/concepts/resumability).
- **Retryable** If a Task fails, it will be retried. You can configure how (or if) a Task is retried. Full details in the [io SDK reference](/sdk/io).
- **Logging** Tasks are logged, so you can see what happened in a Run. Find out more about [viewing runs](/documentation/guides/viewing-runs).
## Subtasks
A Task can have multiple subtasks, and so on. This is useful for breaking down a large Task into smaller Tasks. We currently support nesting 5 levels deep.
## Task Keys
The first param of all Tasks is a `key`. This is a unique identifier for the Task inside that Run. It is used for resumability and logging. It is also used to identify the Task in the [Viewing Runs Dashboard](/documentation/guides/viewing-runs).
## References
<CardGroup cols={2}>
<Card
title="Resumability"
icon="clock"
href="/documentation/concepts/resumability"
>
Runs can be very long-running. Learn how we handle this.
</Card>
<Card
title="Integrations"
icon="grid-2"
href="/documentation/concepts/integrations"
>
Integrations utilize Tasks.
</Card>
<Card title="`io` SDK Reference" icon="wrench" href="/sdk/io">
The `io` object allows you to easily run a Task yourself.
</Card>
<Card
title="Viewing Runs Dashboard"
icon="globe"
href="/documentation/guides/viewing-runs"
>
View all Runs for a Job, all the way down to individual Tasks.
</Card>
</CardGroup>
@@ -0,0 +1,130 @@
---
title: "DynamicTrigger & DynamicSchedule"
sidebarTitle: "Dynamic"
description: "These are advanced features that allows you to attach dynamic triggers to a Job."
---
Sometimes you don't know when you write the code what the trigger or schedule will be.
[DynamicTrigger](/sdk/dynamictrigger) and [DynamicSchedule](/sdk/dynamicschedule) allow you to configure triggers and schedules at runtime.
## DynamicSchedule
```typescript
//1. create a DynamicSchedule
const dynamicSchedule = new DynamicSchedule(client, {
id: "dynamicinterval",
});
//2. create a Job that is attached to the dynamic schedule
new Job(client, {
id: "user-dynamicinterval",
name: "User Dynamic Interval",
version: "0.1.1",
//3. set the DynamicSchedule as the Trigger
trigger: dynamicSchedule,
run: async (payload, io, ctx) => {
await io.logger.info("The userId is ", ctx.source.id);
},
});
//4. Register the DynamicSchedule anywhere in your app
async function registerUserCronJob(userId: string, userSchedule: string) {
//use the userId as the id for the DynamicSchedule
//so it comes through to run() in the context source.id
await dynamicSchedule.register(userId, {
type: "cron",
options: {
cron: userSchedule,
},
});
}
//5. Register inside other Jobs
new Job(client, {
id: "register-dynamicinterval",
name: "Register Dynamic Interval",
version: "0.1.1",
trigger: eventTrigger({
name: "dynamic.interval",
schema: z.object({
userId: z.string(),
seconds: z.number().int().positive(),
}),
}),
run: async (payload, io, ctx) => {
//6. Register the DynamicSchedule
await io.registerInterval("📆", dynamicSchedule, payload.userId, {
seconds: payload.seconds,
});
await io.wait("wait", 60);
//7. Unregister the DynamicSchedule if you want
await io.unregisterInterval("❌📆", dynamicSchedule, payload.id);
},
});
```
## DynamicTrigger
```typescript
//1. create a DynamicTrigger
const dynamicOnIssueOpenedTrigger = new DynamicTrigger(client, {
id: "github-issue-opened",
event: events.onIssueOpened,
source: github.sources.repo,
});
//2. create a Job that is attached to the dynamic trigger
new Job(client, {
id: "listen-for-dynamic-trigger",
name: "Listen for dynamic trigger",
version: "0.1.1",
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on repo: ${
payload.issue.html_url
}. \n\n${JSON.stringify(ctx)}`,
channel: "C04GWUTDC3W",
});
},
});
//3. Register the DynamicTrigger anywhere in your app
async function registerRepo(owner: string, repo: string) {
//the first param (key) should be unique
await dynamicOnIssueOpenedTrigger.register(`${owner}/${repo}`, {
owner,
repo,
});
}
//4. Register inside other Jobs
new Job(client, {
id: "new-repo",
name: "New repo",
version: "0.1.1",
//5. when a new repo is created in your org
trigger: github.triggers.org({
event: events.onNewRepository,
org: "triggerdotdev",
}),
run: async (payload, io, ctx) => {
//6. Register the dynamic trigger so you get notified when an issue is opened
return await io.registerTrigger(
"register-repo",
dynamicOnIssueOpenedTrigger,
payload.repository.name,
{
owner: payload.repository.owner.login,
repo: payload.repository.name,
}
);
},
});
```
@@ -0,0 +1,151 @@
---
title: "Event triggers"
sidebarTitle: "Events"
description: "Event triggers allow you to run Jobs from your own code (or your other Jobs)"
---
> Send an event and any Jobs that subscribe to that event will get triggered.
## Name and Schemas
### Name
Event triggers have a `name`. They will only get triggered when an event with that name are sent.
### Schema
Event triggers take a [Zod](https://github.com/colinhacks/zod) schema. This is used to validate the data that is sent with the event. If the data does not match the schema, the Job will not run.
It also means that inside your run function the payload will be typed correctly. We use [Zod](https://github.com/colinhacks/zod#installation) for our schemas it's a fantastic library that allows you to define schemas in a very simple way.
You can always start out by using `z.any()` as your schema, and then later on you can add more strict validation. See our [Zod guide](/guides/zod) for more information.
## Example
```ts
new Job(client, {
id: "new-user-slack",
name: "New user slack message",
version: "0.1.0",
trigger: eventTrigger({
name: "user.created",
schema: z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
}),
filter: {
//only run the Job if the user is paying
paidPlan: [true],
},
}),
integrations: {
slack,
},
//this function is run when the custom event is received
run: async (payload, io, ctx) => {
//send a message to the #new-users Slack channel with user details
const response = await io.slack.postMessage("send-to-slack", {
channel: "CUKJXJZ3Z",
text: `New user: ${payload.name} (${payload.email}) signed up. ${
payload.paidPlan ? "They are paying" : "They are on the free plan"
}.`,
});
return response.message;
},
});
```
<Note>
You can subscribe to the same event from multiple different Jobs. This is
useful if you want to send an event to multiple different services or if you
want to keep each Job small and simple.
</Note>
## Sending events
There are two ways of sending an event that will Trigger a Job.
### 1. From your own code
You can use `client.sendEvent()` to send an event from your own code. [View the SDK reference](/sdk/triggerclient/instancemethods/sendevent).
```ts
//somewhere in your code, can even be on another server
await client.sendEvent({
name: "user.created",
payload: { name: "John Doe", email: "john@doe.com", paidPlan: true },
});
```
### 2. From another Job
You can use `io.sendEvent()` to send events from inside a Job run, to trigger another. [View the SDK reference](/sdk/io/sendevent).
```ts
new Job(client, {
id: "event-1",
name: "Run when the foo.bar event happens",
version: "0.0.1",
trigger: eventTrigger({
name: "foo.bar",
schema: z.object({
url: z.string(),
}),
}),
run: async (payload, io, ctx) => {
//send an event using `io`
await io.sendEvent("send event", {
name: "user.created",
payload: { name: "Rick Astley", email: "rick.astley@gmail.com" },
});
},
});
```
## Event filters
They are declarative pattern-matching rules, modeled after [AWS EventBridge patterns](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html).
Given the following custom event payload:
```json
{
"uid": "jexAgaeJFJsrGfans1pxqm",
"type": "15 Min Meeting",
"price": 0,
"title": "15 Min Meeting between Eric Allam and John Doe",
"length": 15,
"status": "ACCEPTED",
"endTime": "2023-01-25T16:00:00Z",
"bookingId": 198052,
"organizer": {
"id": 32794,
"name": "Eric Allam",
"email": "eric@trigger.dev",
"language": { "locale": "en" },
"timeZone": "Europe/London"
}
}
```
The following event filter would match the event:
```json
{
"type": ["15 Min Meeting"],
"status": ["ACCEPTED", "REJECTED"],
"organizer": {
"name": ["Eric Allam"]
}
}
```
For an event pattern to match an event, the event must contain all the field names listed in the event pattern. The field names must also appear in the event with the same nesting structure.
The value of each field name in the event pattern must be an array of strings, numbers, or booleans. The event pattern matches the event if the value of the field name in the event is equal to any of the values in the array.
Effectively, each array is an OR condition, and the entire event pattern is an AND condition.
So the above event filter will match because `status == "ACCEPTED"`, and it would also match if `status == "REJECTED"`.
@@ -0,0 +1,37 @@
---
title: Introduction
description: "A Trigger is what starts a Job Run. It can be a webhook, a schedule, or an event."
---
We currently support three types of Triggers: Webhooks, Scheduled, and Events. You can use any of these to start a Job Run.
<CardGroup>
<Card
title="Webhooks"
icon="webhook"
href="/documentation/concepts/triggers/webhooks"
>
Start your Jobs in realtime when events happen in APIs
</Card>
<Card
title="Scheduled"
icon="calendar"
href="/documentation/concepts/triggers/scheduled"
>
Run a Job on a repeating schedule
</Card>
<Card
title="Event"
icon="brackets-curly"
href="/documentation/concepts/triggers/events"
>
Run your Job when you send events with data
</Card>
<Card
title="DynamicTrigger & DynamicSchedule"
icon="code"
href="/documentation/concepts/triggers/dynamic"
>
Create Triggers and Schedules with dynamic settings
</Card>
</CardGroup>
@@ -0,0 +1,61 @@
---
title: "Scheduled triggers"
sidebarTitle: "Scheduled"
description: "Run a Job on a recurring schedule"
---
A Scheduled Trigger runs a Job on a repeated schedule. You can set the schedule using a CRON expression or an interval.
<Snippet file="scheduled-dev-warning.mdx" />
## Interval
This job will run every 60 seconds, starting 60 seconds after this Job is first indexed. Note that it does not run at the top of every minute, but rather 60 seconds after the Job is first indexed.
```ts
import { Job, intervalTrigger } from "@trigger.dev/sdk";
new Job(client, {
id: "scheduled-job-1",
name: "Scheduled Job 1",
version: "0.1.1",
trigger: intervalTrigger({
seconds: 60,
}),
run: async (payload, io, ctx) => {
await io.logger.info("Received the scheduled event", {
payload,
});
return { foo: "bar" };
},
});
```
## Using CRON syntax
If you want a Job to run at a specific time or on a specific day of the week, you can use a CRON expression.
This job will run at 2:30pm every Monday. You can get help with [CRON syntax](https://crontab.guru/#30_14_*_*_1).
<Note>These are not in local time, they are UTC.</Note>
```ts
import { Job, cronTrigger } from "@trigger.dev/sdk";
new Job(client, {
id: "scheduled-job-2",
name: "Scheduled Job 2",
version: "0.1.1",
trigger: cronTrigger({
cron: "30 14 * * 1",
}),
run: async (payload, io, ctx) => {
await io.logger.info("Received the scheduled event", {
payload,
});
return { foo: "bar" };
},
});
```
@@ -0,0 +1,54 @@
---
title: "Webhooks"
sidebarTitle: "Webhooks"
description: "Webhooks allow you to subscribe to events from APIs you use."
---
Webhooks are a crucial part of API development, allowing for real-time reactions to various events across different systems, such as when a Stripe Payment is made, or when a GitHub issue is created.
## Advantages of using Trigger.dev for webhooks
Webhooks can be difficult to work with, especially when developing locally. We make them far easier to use with our [Integrations](/integrations).
- You don't need to register/unregister for webhooks, we do it for you
- We receive the webhook, then keep trying to send it to you until you receive it. If your server goes down, no problem.
## Usage
There are two ways to use webhooks with Trigger.dev:
1. Use one of our built-in Integrations, such as [GitHub](/integrations/github). We'll take care of registering the webhook for you.
2. [Create your own Integration](/integrations/create) that registers for webhooks, this is useful if you want to use a service that we don't have an Integration for.
## Example
```ts Github
import { Job } from "@trigger.dev/sdk";
import { Github, events } from "@trigger.dev/github";
//GitHub integration with API Key (it supports OAuth too)
const github = new Github({
id: "github",
token: process.env.GITHUB_API_KEY!,
});
new Job(client, {
id: "critical-issue-alert",
name: "Critical Issue Alert",
version: "0.1.0",
//When a GitHub issue is modified on the triggerdotdev/trigger.dev repo
trigger: github.triggers.repo({
event: events.onIssue,
owner: "triggerdotdev",
repo: "trigger.dev",
}),
//include any integrations you want to use
integrations: {
slack,
},
//this function gets executed when the webhook is received
run: async (payload, io, ctx) => {
await io.logger.info(`Action was ${payload.action}`);
},
});
```
@@ -0,0 +1,105 @@
---
title: "What is Trigger.dev?"
sidebarTitle: "What is Trigger.dev?"
---
Trigger.dev is a platform, SDK and API for building and running Jobs in your codebase, triggered by various sources, but without having to worry about managing any complicated orchestration infrastructure.
It can be used from _any_ Node.js (support versions) or TypeScript backend application (including serverless applications and microservices).
## What we take care of for you:
- We make it possible to run long-running Jobs on serverless platforms that have short timeouts (e.g. 30 seconds).
- We provide an SDK for building Jobs in your codebase, triggered by various sources such as [events](/triggers/events), [scheduled events](/triggers/scheduled-events), and [webhooks](/triggers/webhooks).
- We provide an orchestration platform for running Jobs in your codebase.
- We provide out-of-the-box Integrations with popular services such as [Slack](/integrations/apis/slack), [OpenAI](/integrations/apis/openai), [GitHub](/integrations/apis/github) and [more](/integrations), which vastly simplifies the process interacting with 3rd-party services.
- We handle OAuth for you
- We provide a nice UI for viewing and debugging your Jobs.
## What you take care of:
- You write your Jobs in your codebase.
- You get a Trigger.dev API Key and add it to your codebase.
- You deploy your codebase.
## How it works
To get an idea of how Trigger.dev works, let's take a look at a simple Job that sends a Slack message when a GitHub issue is labelled as `critical`:
```ts
import { Job } from "@trigger.dev/sdk";
import { Github, events } from "@trigger.dev/github";
import { Slack } from "@trigger.dev/slack";
//GitHub integration with API Key (it supports OAuth too)
const github = new Github({
id: "github",
token: process.env.GITHUB_API_KEY!,
});
//Slack integration with OAuth
const slack = new Slack({
id: "slack",
});
new Job(client, {
id: "critical-issue-alert",
name: "Critical Issue Alert",
version: "0.1.0",
//When a GitHub issue is modified on the triggerdotdev/trigger.dev repo
trigger: github.triggers.repo({
event: events.onIssue,
owner: "triggerdotdev",
repo: "trigger.dev",
}),
//include any integrations you want to use
integrations: {
slack,
},
//this function gets executed when the trigger fires
run: async (payload, io, ctx) => {
await io.logger.info(`Action was ${payload.action}`);
if (payload.action === "labeled" && payload.label?.name === "critical") {
//use the Slack integration to post a message
await io.slack.postMessage("post message", {
channel: "C04GWUTDC3W",
text: `Issue ${payload.issue.number}: ${payload.issue.title} is critical!`,
});
}
},
});
```
This code lives in a file inside your Next.js repo.
It is listening for the [issueEvent](/integrations/apis/github/triggers) GitHub webhook, and when it receives one, we will take care of calling the `run` function supplied to the `Job` constructor with the webhook payload. This gives you the following advantages over traditional webhooks:
- We will automatically register the webhook with GitHub for you, and verify the payload signature.
- We provide a nicely typed `event` payload to your `run` function, so you don't have to setup webhook payload types.
- If your server isn't running, we will wait until it's back online before attempting to run the Job.
- It is very easy to test your Job locally using our [Test Run](/guides/running-tests) feature.
As you can see the above Job also makes a call to our Slack [postMessage](/integrations/apis/slack) function, which provides the following advantages over using the raw Slack API:
- We automatically handle the OAuth flow for you, so you don't have to worry about setting up a Slack app and dealing with credentials in your code (see our [Authentication](/concepts/authentication) guide for more details).
- We will automatically retry the request if the Slack API returns an error.
- We provide a nicely typed `response` object from your `postMessage` function, so you don't have to setup Slack API types.
## Why use Trigger.dev?
Apart from the reasons mentioned above, there are a few other reasons why you might want to use Trigger.dev:
- You want to access your database or other internal services from your Jobs, without having to expose them to the internet.
- You want to colocate your Jobs with your code, so you can deploy them together in one atomic unit.
- You want to build event-driven architectures without having to manage any complicated orchestration infrastructure.
- You want to add in delays or retries to your Jobs, without having to worry about managing a queue.
## Architecture
Below is a simplified architecture diagram of how Trigger.dev works:
{/* https://www.tldraw.com/r/v2_KEeyTalIH0NRKdsb01Lqt?viewport=36%2C-165%2C2234%2C1420&page=page%3AOecar06rEOb6Kpu9XzDKo */}
![Architecture](/images/architecture.png)
As you can see above, we communicate between your code and the Trigger.dev platform. This allows us to send events to your code, and receive tasks from your code.
+83
View File
@@ -0,0 +1,83 @@
---
title: "FAQ"
description: "This section is aimed at collecting common questions from users to provide documented answers."
---
<Accordion title="Can I self-host Trigger.dev?">
Yes, view our [self-hosting guide](/documentation/guides/self-hosting).
</Accordion>
<Accordion title="Does my data get sent to your servers?">
Only what you choose to send. The main body of your Job code runs on your
infrastructure. For example when you do a database query, that never touches
us. We receive data that triggers the start of a Job, any data you pass to one
of our API Integrations, and any data you choose to log using our logging
function. We store this to display on the Runs page of your dashboard.
</Accordion>
<Accordion title="Does Trigger.dev only support Next.js?">
No, you'll be able to use Trigger.dev with a variety of other frameworks,
serverless or otherwise, such as Node.js, Remix, and Express, in addition to
Next.js.
</Accordion>
<Accordion title="How is this different to Zapier, Pipedream etc?">
Trigger.dev is a code-first tool that lets you create Jobs directly in your
code, rather than using a UI builder like Zapier. This means you can stay in
your own IDE and keep your internal data secure.
</Accordion>
<Accordion title="Can I run Trigger.dev locally?">
Yes. Jobs are created in your code locally.
</Accordion>
<Accordion title="Can I build complex Jobs?">
Yes. Theres no limit to the complexity of Jobs you can create. Jobs are
created in code so you can write conditional, looping, branching or time
delayed logic.
</Accordion>
<Accordion title="What is a 'Run'?">
A run is a single execution of a Job. This can be in either development,
staging or production. [View full details](/documentation/concepts/runs).
</Accordion>
<Accordion title="Can I use version control or roll-backs?">
Yes. You create Jobs directly in your own code so its version controlled with
everything else.
</Accordion>
<Accordion title="How long does it take to code up a Job?">
A simple Job doing a couple of API calls from different services will take
about 5 minutes to create.
</Accordion>
<Accordion title="Do you have all the Integrations I need?">
View [our Integrations page](/integrations) to see the Integrations we
currently support. If we don't have an Integration you need, you can request
it or create it yourself.
</Accordion>
<Accordion title="Is Trigger.dev open source?">
Yes, Trigger.dev is open source. We are strong supporters of open source
software, and our first product, [jsonhero.io](https://jsonhero.io), has a
thriving open source community. Trigger.dev follows in that tradition.
</Accordion>
<Accordion title="Is Trigger.dev a no/low-code tool?">
No. Trigger.dev is designed for developers who want to create Jobs directly in
code, without using a UI builder like Zapier. This allows developers to stay
in their familiar development environment and customise their Jobs with code.
</Accordion>
<Accordion title="Can non-coders use this product?">
Developers will need to create Jobs. However, anyone on the team can monitor
running Jobs in the Trigger.dev dashboard.
</Accordion>
<Accordion title="My question is not listed here">
Hop in our [Discord](https://discord.gg/nkqV9xBYWy) and ask any question you have in there!
You could also open a [GitHub issue](https://github.com/triggerdotdev/trigger.dev/issues/new).
</Accordion>
+26
View File
@@ -0,0 +1,26 @@
---
title: "Get in touch with us!"
sidebarTitle: "Get help"
description: "Contact us if you have any questions, or would like help with any issues."
---
<Note>We are active in the community and will respond to all messages.</Note>
Please email us at <a href="mailto:help@trigger.dev">help@trigger.dev</a> or choose one of the options below:
<CardGroup>
<Card
title="Join the community"
icon="discord"
href="https://discord.gg/nkqV9xBYWy"
>
The place to meet other users, the team and to get product updates.
</Card>
<Card
title="Schedule a call"
icon="video"
href="https://cal.com/team/triggerdotdev/founders-call"
>
A call with someone from our team. We are happy to help.
</Card>
</CardGroup>
+109
View File
@@ -0,0 +1,109 @@
---
title: "Using the CLI"
description: "How to use the CLI"
---
## Introduction
If you're setting up your project for the first time, we recommend using the CLI by following the [Quickstart guide](/documentation/quickstart).
## init Command
The `init` command incorporates Trigger.dev into your Next.js project. It performs the following functions:
- Adds Trigger.dev to the project.
- Creates a new route.
- Generates an example file.
During execution, this command requires some configuration parameters. Detailed information on these parameters can be found in the next section.
![Your first Job](/images/cli-init.gif)
Run this `init` command in a terminal window to setup your project with Trigger.dev
<CodeGroup>
```bash npm
npx @trigger.dev/cli@latest init
```
```bash pnpm
pnpm dlx @trigger.dev/cli@latest init
```
```bash yarn
yarn dlx @trigger.dev/cli@latest init
```
</CodeGroup>
### CLI steps explained
<AccordionGroup>
<Accordion title="Are you using the Trigger.dev cloud or self-hosted?">
<ResponseField name="Option 1: Trigger.dev Cloud">
Trigger.dev Cloud is a fully hosted service that provides the easiest and quickest way to use Trigger.dev.
</ResponseField>
<ResponseField name="Option 2: Self-hosted">
Instead of using the Trigger.dev Cloud service which we host for you, you
can host the Trigger.dev platform yourself. We provide an official
Trigger.dev Docker image you can use to easily self-host on your preferred
platform. More information on self-hosting can be found in the
self-hosting section of the docs
[here](/documentation/guides/self-hosting).
<Accordion title="Enter the URL of your self-hosted Trigger.dev instance">
When following the [self-hosting guide](/documentation/guides/self-hosting) you will deploy your Docker image to a platform of your choice. After completing this step, enter the URL of your self-hosted instance in this CLI step.
</Accordion>
</ResponseField>
</Accordion>
<Accordion title="Enter your development API key">
To locate your development API key, login to the [Trigger.dev
dashboard](https://cloud.trigger.dev) and select the Project you want to
connect to. Then click on the Environments & API Keys tab in the left menu.
You can copy your development API Key from the field at the top of this page.
(Your development key will start with `tr_dev_`).
</Accordion>
<Accordion title="Enter a unique ID for your endpoint">
Enter a custom ID or use the default by hitting enter. You can learn more
about endpoints
[here](/documentation/concepts/environments-endpoints#endpoints).
</Accordion>
</AccordionGroup>
## dev Command
Once you're running your Next.js project locally, you can then execute the `dev` CLI command to run Trigger.dev locally. You should run this command every time you want to use Trigger.dev locally.
![Your first Job](/images/cli-dev.gif)
<Warning>
Make sure your Next.js site is running locally before continuing. You must
also leave this `dev` terminal command running while you develop.
</Warning>
In a **new terminal window or tab** run:
<CodeGroup>
```bash npm
npx @trigger.dev/cli@latest dev
```
```bash pnpm
pnpm dlx @trigger.dev/cli@latest dev
```
```bash yarn
yarn dlx @trigger.dev/cli@latest dev
```
</CodeGroup>
<br />
<Note>
You can optionally pass the port if you're not running on 3000 by adding
`--port 3001` to the end
</Note>
@@ -0,0 +1,37 @@
---
title: "Contributing"
description: "You can contribute Integrations and core code to the Trigger.dev platform "
---
Trigger.dev is open source and we welcome contributions from the community.
<CardGroup>
<Card
title="GitHub Repo"
icon="github"
href="https://github.com/triggerdotdev/trigger.dev"
>
Our Open Source GitHub repo
</Card>
<Card
title="View and create issues"
icon="bug"
href="https://github.com/triggerdotdev/trigger.dev/issues"
>
You can view open issues and create new ones
</Card>
<Card
title="Create an Integration"
icon="square-plus"
href="/integrations/create"
>
Create an Integration for your own use or as a public package.
</Card>
<Card
title="Join the community"
icon="discord"
href="https://discord.gg/nkqV9xBYWy"
>
The place to meet other users, the team and to get product updates.
</Card>
</CardGroup>
+272
View File
@@ -0,0 +1,272 @@
---
title: "Create a Job"
description: "How to create a Job in your codebase"
---
> Jobs are the core of the system. They allow you to run code when some event occurs. They are built using a combination of Triggers and Tasks.
### Pre-requisites
Make sure your Project is set up with Trigger.dev. We recommend [using the CLI](/documentation/quickstart) to do this.
## How to write a Job in code
### 1. Create a Job file in your Project
This is where you will write your Job code. E.g. `my-job.ts`.
```ts
//this path might be different depending on your project
import { client } from "@/trigger";
client.defineJob({
// This is the unique ID for your Job's end-point
id: "your-job-id",
// This is the name of your Job
name: "Your Job name",
// This is the version of our SDK you are using
version: "0.0.1",
...
```
The `id` and `name` are important because they are used to create and identify your Job in the app.
<Note>
This Job must be imported in the `trigger` file in order to be registered when
the CLI dev command is run. This can be found in either the
`app/api/trigger/route.ts` file if you're using the Next.js App Router, or
`pages/api/trigger.ts` if you're using the Next.js Pages Router.
</Note>
### 2. Choose a Trigger
This is what kicks-off a Job. There are a few different types of Triggers you can use:
<Tabs>
<Tab title="Scheduled">
Run a Job on a repeating schedule, using [intervalTrigger](/documentation/concepts/triggers/scheduled#interval)
```ts
client.defineJob({
...
trigger: intervalTrigger({
seconds: 60,
}),
...
```
Or with CRON syntax, using [cronTrigger](/documentation/concepts/triggers/scheduled#using-cron-syntax):
```ts
client.defineJob({
...
trigger: cronTrigger({
cron: "30 14 * * 1",
}),
...
```
</Tab>
<Tab title="Webhook">
Start your Jobs when an event happens in another API. You'll need to use [Integrations](/integrations) to do this.
Here's an example with the GitHub integration.
```ts
client.defineJob({
...
//E.g. When a GitHub issue is modified on the triggerdotdev/trigger.dev repo
trigger: github.triggers.repo({
event: events.onIssue,
owner: "triggerdotdev",
repo: "trigger.dev",
}),
...
```
</Tab>
<Tab title="Event">
The [eventTrigger](/documentation/concepts/triggers/events) allows you to define an event that your Job listens for.
When you [send an event](/documentation/concepts/triggers/events#sending-events) with the same name the Job will run.
``` ts
client.defineJob({
...
//E.g. when a user is created in your app (you send the event)
trigger: eventTrigger({
name: "user.created",
schema: z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
}),
...
```
</Tab>
<Tab title="Dynamic">
These are advanced features that allows you to attach dynamic triggers to a Job. Full information [here](/documentation/concepts/triggers/dynamic).
</Tab>
</Tabs>
### 3. Create the Job Tasks
> A Task is a resumable unit of a Run that can be retried, resumed and is logged.
<Info>
You can use just regular code in your Jobs. But you don't get the benefits of
retrying, logging and resumability. More info on [Tasks vs regular
code](/documentation/concepts/tasks#tasks-vs-regular-code).
</Info>
You can string together multiple Tasks and regular code in any order you want.
**Useful built-in Tasks:**
| Task | Description | Task code |
| ------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [Delay](/documentation/concepts/delays) | Wait for a period of time | `await io.wait("wait", 60);` |
| [Log](/sdk/io/logger) | Log a message | `await io.logger.log("Hello");` |
| [Send Event](/sdk/io/sendevent) | Send an event (for eventTrigger) | `await io.sendEvent("my-event", { name: "my.event", payload: { hello: "world" } });` |
| [Run task](/sdk/io/runtask) | Wrap your own code in this to create a Task | `await io.runTask("My Task", { name: "My Task" }, async () => { console.log("Hello"); });` |
| [Background fetch](/sdk/io/backgroundfetch) | Fetch data from a URL that can take longer that the serverless timeout. | `await io.backgroundFetch("fetch-some-data", { url: "https://example.com" });` |
For a full list of built-in Tasks, see the [io SDK reference](/sdk/io).
**Integration Task examples:**
<Info>
To use our integrations you will need to set them up in the app first. Our
guide is [here](/documentation/guides/using-integrations).
</Info>
<AccordionGroup>
<Accordion title="OpenAI" description="Generate text completions in a conversational context.">
**Task:** [backgroundCreateCompletion](/integrations/apis/openai)
```ts
await io.openai.backgroundCreateChatCompletion("background-chat-completion", {
model: "gpt-3.5-turbo",
messages: [
{
role: "user",
content: "Create a good programming joke about background jobs",
},
],
});
```
View more OpenAI tasks [here](/integrations/apis/openai).
</Accordion>
<Accordion title="GitHub" description="Add a custom label to a GitHub issue.">
**Task:** [addIssueLabels](/integrations/apis/github-tasks)
```ts
await io.github.addIssueLabels("add label", {
owner: payload.repository.owner.login,
repo: payload.repository.name,
issueNumber: payload.issue.number,
labels: ["bug"],
});
```
View more GitHub tasks [here](/integrations/apis/github-tasks).
</Accordion>
<Accordion title="Resend" description="Send an email using Resend.">
**Task:** [sendEmail](/integrations/apis/resend)
```ts
await io.resend.sendEmail("send-email", {
to: payload.to,
subject: payload.subject,
text: payload.text,
from: "Trigger.dev <hello@email.trigger.dev>",
});
```
</Accordion>
<Accordion title="Slack" description="Post a message to a Slack channel.">
**Task:** [postMessage](/integrations/apis/slack)
```ts
await io.slack.postMessage("post message", {
channel: "C04GWUTDC3W",
text: "My first Slack message",
});
```
View more Slack tasks [here](/integrations/apis/slack).
</Accordion>
</AccordionGroup>
These are just a few examples of Integration Tasks. For many more, browse our [Integrations section](/integrations/).
### 4. Register your Jobs
While your app is running, open a **new terminal window or tab** and run:
<CodeGroup>
```bash npm
npx @trigger.dev/cli@latest dev
```
```bash pnpm
pnpm dlx @trigger.dev/cli@latest dev
```
```bash yarn
yarn dlx @trigger.dev/cli@latest dev
```
</CodeGroup>
This will register all of your Jobs, they should appear in your dashboard.
<Note>
Not seeing your Job in the web app? It might be because you forgot to import
it. This will need to be either in `app/api/trigger/route.ts` file if you're
using the Next,js App Router, or `pages/api/trigger.ts` if you're using the
Next,js Pages Router.
</Note>
If you are having trouble getting your job running, please reach out to us and we will help you fix any issues:
- [Join our Discord](https://discord.gg/kA47vcd8P6)
- [Email us](mailto:help@trigger.dev)
---
## Next steps
We recommend exploring all of the below sections to fully understand how to create and run Jobs using Trigger.dev.
<CardGroup cols={2}>
<Card
title="Running your jobs"
icon="wand-magic-sparkles"
href="/documentation/guides/running-jobs"
>
A guide for how to run your Jobs. Triggering a test Run and Triggering your Job for real
</Card>
<Card title="Example Jobs" icon="slot-machine" href="/examples">
View example Jobs / the example jobs repo. These are a great starting
point for creating your own Jobs.
</Card>
<Card title="SDK reference" icon="code" href="/sdk">
How to use the SDK. This includes all the available Tasks, triggers and
actions you can use.
</Card>
<Card title="Integrations" icon="grid-2" href="/integrations">
Integrations make it easy to authenticate and use APIs.
Learn how to use and create integrations.
</Card>
</CardGroup>
@@ -0,0 +1,46 @@
---
title: "Automatic endpoint refreshing"
description: "Use our webhook to automatically tell us when you deploy"
---
## In the dashboard
1. Go to the "Environments & API Keys" page in your Trigger.dev dashboard
![Go to the Environments & API Keys page ](/images/environments-link.png)
2. Select your endpoint row in the table of endpoints.
![Select your endpoint](/images/endpoint-manual-refresh-1.png)
3. There is a webhook URL in the "Automatic Refresh" section. Copy this URL.
![Copy the webhook URL](/images/endpoint-automatic-refresh.png)
## How to use the webhook URL
### Vercel
You can use this webhook URL in your Vercel dashboard, so when a deployment succeeds it automatically refreshes the endpoint.
1. From your Vercel team dashboard, select "Settings"
![Go to your Team's settings](/images/deploy-vercel-1.png)
2. Go to the "Webhooks" page
![Go to the Webhooks page](/images/deploy-vercel-2.png)
3. Select the "Deployment Succeeded" event, your Vercel Project, and paste in our webhook URL. Then "Create Webhook".
![Fill in the webhook details](/images/deploy-vercel-3.png)
4. You're done! Whenever a deploy succeeds, Vercel will tell us to refresh your endpoint.
![You're done!](/images/deploy-vercel-4.png)
### GitHub Actions
You can add a step to the GitHub Action that deploys your app.
```yaml .github/workflows/release.yml
- name: 🚀 Refresh Trigger.dev Jobs
env:
DEPLOY_TEST_HOOK: ${{ secrets.TRIGGER_ENDPOINT_HOOK }}
run: |
curl -X POST $TRIGGER_ENDPOINT_HOOK
```
You will need to [setup the `TRIGGER_ENDPOINT_HOOK` secret in your GitHub repo](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository). Set it to the webhook URL from the endpoint panel.
### Do an HTTP request yourself
Any `GET` or `POST` request to the webhook URL will cause a refresh of the Jobs for that endpoint. `POST` requests don't need a body. There is no authentication required.
@@ -0,0 +1,13 @@
---
title: "Manual endpoint refreshing"
description: "You can manually refresh Endpoints in your Trigger.dev dashboard"
---
1. Go to the "Environments & API Keys" page in your Trigger.dev dashboard
![Go to the Environments & API Keys page ](/images/environments-link.png)
2. Select your endpoint row in the table of endpoints.
![Select your endpoint](/images/endpoint-manual-refresh-1.png)
3. Click the "Refresh now" button
![Refresh the PROD endpoint](/images/endpoint-manual-refresh-2.png)
You should see the "Last refreshed" date update to the current time.
@@ -0,0 +1,55 @@
---
title: "First-time setup"
description: "The first time you deploy to a new environment you will need to setup the endpoint for that environment."
---
## 1. Make sure your Client is configured correctly
The API Key you use for your `Client` is how we know which environment to run your code against:
```ts
export const client = new TriggerClient({
id: "nextjs-example",
//this environment variable should be set to your DEV API Key locally,
//and your PROD API Key in production
apiKey: process.env.TRIGGER_API_KEY!,
});
```
## 2. Set your environment variables on your server
Ensure that your `TRIGGER_API_KEY` (or whatever you've set it to) environment variable is set on your server, to the correct value from the "Environments & API Keys" page in your Trigger.dev dashboard.
![Select your prod API key](/images/api-keys-prod.png)
If you've used any other environment variables in your code (like API Keys for other services), make sure they are set on your server too.
The exact instructions will vary depending on where you deploy to. Here are the docs links to some popular services:
- [Vercel](https://vercel.com/docs/concepts/projects/environment-variables#)
- [Cloudflare Pages](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables)
- [Cloudflare Workers](https://developers.cloudflare.com/workers/platform/environment-variables/)
- [Netlify](https://docs.netlify.com/environment-variables/overview/)
## 3. Deploy your code as usual
Your Job code lives in your codebase, so you can deploy it as you normally would.
## 4. Connect the Endpoint for the first time
1. Go to the "Environments & API Keys" page in your Trigger.dev dashboard
![Go to the Environments & API Keys page ](/images/environments-link.png)
2. Click the "Configure" button on the relevant endpoint
![Select your endpoint](/images/endpoint-setup-1.png)
3. Enter the URL of your Trigger.dev endpoint
![Enter the URL of your Trigger.dev endpoint](/images/endpoint-setup-2.png)
<Info>
This is the value of the `path` option for the adaptor you're using. For
Next.js, this would usually be `https://yourdomain.com/api/trigger`.
</Info>
4. Click "Save". This will automatically connect, test your endpoint, and register all your Jobs.
![Save](/images/endpoint-setup-3.png)
+42
View File
@@ -0,0 +1,42 @@
---
title: "Introduction"
description: "A guide for how to deploy your Jobs"
---
Deployment uses [Environments & Endpoints](/documentation/concepts/environments-endpoints) to connect your Jobs to the Trigger.dev platform.
## First time deploying to a new Environment
The first time you deploy to a new environment you will need to setup the
endpoint for that environment.
<Card
title="First time setup"
icon="wrench"
href="/documentation/guides/deployment-setup"
>
This only needs to be done once for each environment
</Card>
## Subsequent deployments (i.e. refreshing your Endpoint)
If you add new Jobs, change the id of a Job, or change the Triggers of a Job then you will need to refresh your Endpoint.
There are two ways to do this:
<CardGroup cols={2}>
<Card
title="Manual refreshing"
icon="arrow-pointer"
href="/documentation/guides/deployment-manual"
>
Manually refresh in your Trigger.dev dashboard
</Card>
<Card
title="Automatic refreshing"
icon="robot"
href="/documentation/guides/deployment-automatic"
>
Automatically refresh by using our webhook
</Card>
</CardGroup>
@@ -0,0 +1,49 @@
---
title: "Managing Jobs"
description: "How to view, add, modify, and delete jobs"
---
[Jobs](/documentation/concepts/jobs) live inside a [Project](/documentation/concepts/projects).
For Jobs to actually run, they need to be connected to the Trigger.dev platform.
## Viewing Jobs
The main page for a Project is the list of connected Jobs. You can see overview details for a each Job here, including details on the Trigger, Integrations and when it was last run.
<Frame caption="The homepage of a Project">
![Populated Jobs Page](/images/project-jobs.png)
</Frame>
## How to add your first Job
We recommend you do the initial setup of a new Project by following our [quick start guide](/documentation/quickstart). Once completed you should have an example Job in your dashboard.
## Adding more Jobs locally
When you add a new Job to your codebase, you'll need to connect it to Trigger.dev. This is achieved by refreshing the endpoint in your local Dev environment.
1. Go to the "Environments & API Keys" page for your Project
![Click "Environments & API Keys"](/images/environments-link.png)
2. Select the "DEV" row in the table of endpoints.
![Environments & API Keys](/images/environments-click-dev.png)
3. Click the "Refresh now" button
![Refresh the DEV endpoint](/images/endpoint-refresh.png)
<Note>
You only need to do this when you add a new Job (a job with an ID that hasn't
been connected to Trigger.dev before). If you modify an existing Job, you
don't need to refresh the endpoint.
</Note>
## Deploying Jobs
See our [deployment guide](/documentation/guides/deployment).
## Disabling Jobs
Sometimes there's a Job that you no longer want to be Triggered. You can achieve this by Disabling it. This will prevent the `run()` function being called for that Job.
## Archiving Jobs
<Note>Being able to archive Jobs is coming soon</Note>
@@ -0,0 +1,6 @@
---
title: "Express"
description: "How to get setup and deploy your Express project"
---
<Note>Support for Express is coming very soon</Note>
@@ -0,0 +1,94 @@
---
title: "Next.js"
description: "How to get setup and deploy Jobs for your Next.js project"
---
You can write Jobs in your Next.js codebase and deploy to both serverless platforms and long-running servers.
## Supported platforms
We support all platforms because under-the-hood an API endpoint is used to run your Jobs.
## Initial setup
View our [Quick start guide](/documentation/quickstart) to get setup.
## Writing Jobs
View our [guide for writing Jobs](/documentation/guides/create-a-job).
## Deployment
View our [deployment guide](/documentation/guides/deployment) to learn how to deploy your Jobs.
## Middleware
Next.js Middleware allows you to run code before a request is completed, and if you are using it currently in your Next.js project (or you add it later), you might need to guard against altering requests to the `/api/trigger` endpoint, which needs to be exposed to the Trigger.dev installation (either your self-hosted one or the Trigger.dev Cloud).
To make sure you aren't matching the `/api/trigger` route in your middleware, check your `config.matcher` export:
```ts middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL("/home", request.url));
}
// See "Matching Paths" below to learn more
export const config = {
matcher: "/about/:path*",
};
```
The above matcher doesn't match `/api/trigger` so there won't be an issue here. But the following middleware.ts file will cause conflicts with Trigger.dev
```ts middleware.ts
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL("/home", request.url));
}
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
```
As you can see, the last matcher `"/(api|trpc)(.*)"` matches anything starting with `api`, which matches `/api/trigger`. You can add a negative-lookahead pattern to the matcher to exclude the `/api/trigger` route:
```ts middleware.ts
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api((?!/trigger))|trpc)(.*)"],
};
```
The above will match anything starting with `/api` except for `/api/trigger`.
If you want to match all routes except for `/api/trigger`, you can use the following matcher:
```ts middleware.ts
// Match all routes other than /api/trigger
export const config = {
matcher: ["/((?!api/trigger).*)"],
};
```
For more information about middleware matchers, head over to the Next.js [middleware documentation](https://nextjs.org/docs/pages/building-your-application/routing/middleware#matcher).
### Clerk.com Auth Middleware
If you are using [Clerk.com](https://clerk.com) for your Next.js authentication, make sure to update your middleware file to add `/api/trigger` to the `publicRoutes`, like so:
```ts middleware.ts
import { authMiddleware } from "@clerk/nextjs";
// Adding the /api/trigger route to the public routes so clerk doesn't return a 401
// /api/trigger will handle it's own authentication
export default authMiddleware({
publicRoutes: ["/api/trigger"],
});
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
```
+24
View File
@@ -0,0 +1,24 @@
---
title: "Rerunning"
description: "From a Run page, you can rerun"
---
On the Run page when the Run is in a `success` or `failed` state, you can rerun by clicking the `Rerun` button.
![The Rerun button](/images/rerun.png)
There are two possible rerun options.
### 1. Run again
This option is always available (when a run has finished).
It creates a brand new run with the same inputs as the original the same Trigger payload, settings, connections etc.
<Warning>Stating the obvious: this will run all the code again.</Warning>
### 2. Retry job run
This option is only available when the run has failed.
It will continue the existing Run by retrying the Task that failed the Job.
@@ -0,0 +1,37 @@
---
title: "Running your Jobs"
description: "A guide for how to run your Jobs."
---
There are two ways to run your Jobs:
1. Triggering a test Run
2. Triggering your Job for real
## 1. Triggering a test Run
You can perform a Run with any payload you want or use one of our examples on the test page in our dashboard.
[How to do a test Run from the dashboard](/documentation/guides/testing-jobs).
## 2. Triggering your Job for real
Triggering your Job for real depends on the [type of trigger](/documentation/concepts/triggers) you have attached to your Job.
There are three types of Trigger:
### Scheduled Trigger
<Snippet file="scheduled-dev-warning.mdx" />
[Scheduled Triggers](/documentation/concepts/triggers/scheduled) are triggered automatically by our system on the schedule you define. Once you have [deployed](/documentation/guides/deployment) your Job, it will run automatically.
### Event Trigger
[Event Triggers](/documentation/concepts/triggers/event) are triggered by an event that you send from elsewhere in your code.
<Snippet file="send-event-options.mdx" />
### Webhook Trigger
[Webhook Triggers](/documentation/concepts/triggers/webhook) are triggered when you perform the action that the webhook subscribes to. For example, if you have a webhook Trigger that subscribes to the `pull_request` event on GitHub, then it will be triggered whenever a pull request is opened, closed, or updated on the repo you configured.
@@ -0,0 +1,36 @@
---
title: "Self hosting"
description: "You can self-host the Trigger.dev platform"
---
## What does this mean?
Instead of using the [Trigger.dev Cloud](https://cloud.trigger.dev) service which we host for you, you can host the Trigger.dev platform yourself.
## Local development
When you're working on your web app, you can run Trigger.dev locally alongside your web app.
- See our [GitHub docker project](https://github.com/triggerdotdev/docker) for instructions.
- You will need to [setup tunneling](/documentation/guides/tunneling-platform) if you want to receive webhooks from external services.
## Deployment
We provide an official Trigger.dev [docker image](https://github.com/triggerdotdev/trigger.dev/pkgs/container/trigger.dev) you can use to easily self-host on your preferred platform. We also provide guides for some popular platforms:
<CardGroup>
<Card
title="Fly.io"
icon="fly"
href="/documentation/guides/self-hosting/flyio"
>
Easily to deploy to Fly.io
</Card>
<Card
title="Render.com"
icon="draw-square"
href="/documentation/guides/self-hosting/flyio"
>
Easily to deploy to Render.com
</Card>
</CardGroup>
@@ -0,0 +1,143 @@
---
title: "Deploy to Fly.io"
description: "Deploy self hosted version of [Trigger.dev](https://trigger.dev) to Fly.io"
---
You can use this [repository](https://github.com/triggerdotdev/fly.io) as a jumping off point for deploying a self-hosted version of Trigger.dev on Fly.io using the Trigger.dev public docker image located at `ghcr.io/triggerdotdev/trigger.dev:latest`
## Fork the repository mentioned above and change the app name
You should fork the repository before starting so you can make changes and commit them. For example, you'll need to change the `app` property in the `fly.toml` file to be something other than `app = "trigger-v2-fly-demo"`.
## Install and configure the fly.io CLI
1. Install the Fly CLI tool:
<CodeGroup>
```sh Mac
brew install flyctl
```
```sh Windows
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"
```
</CodeGroup>
2. Authenticate the CLI:
```sh
fly auth login
```
## Create the fly.io app and pg db
1. Launch the app:
```sh
fly launch
```
2. Follow the prompts by `fly launch` and make sure to answer them in the following way:
```sh
? Would you like to copy its configuration to the new app? Yes
? Choose an app name (leaving blank will default to 'trigger-v2-fly-demo') <enter your preferred app name here or leave blank>
? Would you like to set up a Postgresql database now? Yes
? Select configuration: Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk <- feel free to pick a beefier machine
? Would you like to set up an Upstash Redis database now? No
? Would you like to deploy now? No
```
## Gather your secret environment variables
### Required
`MAGIC_LINK_SECRET`, `SESSION_SECRET` and `ENCRYPTION_KEY`
All of these secrets should be 16-byte random strings, which you can easily generate (and copy into your pasteboard) with the following command:
```sh
openssl rand -hex 16 | pbcopy
```
`LOGIN_ORIGIN` and `APP_ORIGIN`
Both of these secrets should be set to the base URL of your fly application. For example `https://trigger-v2-fly-demo.fly.dev`
### Optional
`AUTH_GITHUB_CLIENT_ID` and `AUTH_GITHUB_CLIENT_SECRET`
1. If you plan on logging in with GitHub auth, you'll need to create a GitHub OAuth app with the following configuration:
![github oauth](/images/github-oauth.png)
2. Once you register the application you'll need to click on the "Generate new client secret" button:
![github generate new secret](/images/github-generate-new-secret.png)
3. And then you can copy out the AUTH_GITHUB_CLIENT_ID and AUTH_GITHUB_CLIENT_SECRET:
![github copy secrets](/images/github-secrets.png)
`RESEND_API_KEY`, `FROM_EMAIL` and `REPLY_TO_EMAIL`
We use [Resend.com](https://resend.com) for email sending (including the magic-link signup/login system). They have a generous free tier of 100 emails a day that should be sufficient. Signup for Resend.com and enter the environment vars below
## Set the secrets
Call the `fly secrets set` command to stage the secrets to be used on first deploy:
```sh
fly secrets set \
ENCRYPTION_KEY=<random string> \
MAGIC_LINK_SECRET=<random string> \
SESSION_SECRET=<random string> \
LOGIN_ORIGIN="https://<fly app name>.fly.dev" \
APP_ORIGIN="https://<fly app name>.fly.dev" \
FROM_EMAIL="Acme Inc. <hello@yourdomain.com>" \
REPLY_TO_EMAIL="Acme Inc. <reply@yourdomain.com>" \
RESEND_API_KEY=<your API Key> \
AUTH_GITHUB_CLIENT_ID=<your GitHub OAuth Client ID> \
AUTH_GITHUB_CLIENT_SECRET=<your GitHUb OAuth Client Secret>
Secrets are staged for the first deployment
```
## Deploy
Now you can deploy to fly. Here we are setting the machine VM size to use 1 dedicated CPU core with 2 GB of memory, but you can run `fly platform vm-sizes` to see other options. The below app will cost about $30/month.
```sh
fly deploy --vm-size performance-1x
```
## Visit and create an account
Once deployed, you should be able to open `https://<your fly app name>.fly.dev/` in your browser and create an account, either using GitHub or a magic email link.
## Initialize your Next.js project
Next you can easily bootstrap your Next.js project to use your self-hosted instance of Trigger.dev.
First, `cd` into your Next.js project, then run the `@trigger.dev/cli init` command to initialize your Next.js project:
```sh
npx @trigger.dev/cli@latest init -t "https://<your fly app name>.fly.dev"
```
When it asks for your development API key, head over to your self-hosted Trigger.dev dashboard and select the initial project you created when signing up, and head to the `Environments & API Keys` page to copy your `dev` API key:
![api key](/images/api-key.png)
## Start your dev server
Run your Next.js project dev server with `npm run dev` and then in a new terminal window you will need to run the `@trigger.dev/cli dev` command to connect to your Trigger.dev instance and allow it to tunnel to your local Next.js server:
```sh
npx @trigger.dev/cli@latest dev
```
At this point you should be able to navigate to your Trigger.dev dashboard and view your registered jobs
@@ -0,0 +1,72 @@
---
title: "Deploy to Render"
description: "Deploy self hosted version of [Trigger.dev](https://trigger.dev) to [Render](https://render.com/)"
---
You can use this [repository](https://github.com/triggerdotdev/render.com) as a jumping off point for deploying a self-hosted version of Trigger.dev on Render using the Trigger.dev public docker image located at `ghcr.io/triggerdotdev/trigger.dev:latest`
### Fork the repository mentioned above and change the app name
You should fork the repository before starting so you can make changes and commit them. For example, in render.yaml file you'll need to change the name under services to a unique name for your app.
### Create a new web service
1. Go to the Render dashboard.
2. Click on "New" in the top right corner.
3. Choose "Web service"
![new web service render](/images/new-web-service-render.png)
4. Click on "Connect With GitHub" (if you have not already done so). You will need to authorize Render to access your GitHub repositories.
5. Once connected, you will see a list of your repositories. Choose the repository that contains your project.
6. Render will detect the render.yaml file in your repository and auto fill the fields, add your app name now.
![add app name render](/images/add-app-name-render.png)
7. Scrolldown, click on advanced. Here you will need to add the environment variables
![advanced render](/images/advanced-render.png)
### Gather your secret environment variables
The process for generating and collecting the required environment variables is identical to the one described in the [Fly.io guide](/documentation/guides/self-hosting/flyio#gather-your-secret-environment-variables). Follow the steps there to generate and collect the required environment variables.
### Set the environment variables
The render.yaml file declares several environment variables that your app needs to run. These include the `ENCRYPTION_KEY`, `MAGIC_LINK_SECRET` and `SESSION_SECRET`, and others. You need to manually set the values of these environment variables in the Render dashboard:
1. Click on "Add Environment Variable" to add a new environment variable.
2. Enter the key and value for each environment variable declared in your render.yaml file. The key should match the name of the environment variable in the render.yaml file.
![add env variables render](/images/add-env-variables-render.png)
### Deploy
Render automatically deploys your service when you push to the connected GitHub repository. Alternatively, you can manually trigger a deployment from the Render dashboard by clicking "Deploy" in the "Deploys" tab.
### Initialize your Next.js project
Next, you can easily bootstrap your Next.js project to use your self-hosted instance of Trigger.dev.
First, cd into your Next.js project, then run the `@trigger.dev/cli init` command to initialize your Next.js project:
```sh
npx @trigger.dev/cli@latest init -t "https://<your render app name>.onrender.com"
```
When it asks for your development API key, head over to your self-hosted Trigger.dev dashboard and select the initial project you created when signing up, and head to the `Environments & API Keys` page to copy your `dev` API key:
![api key](/images/api-key.png)
### Start your dev server
Run your Next.js project dev server with `npm run dev` and then in a new terminal window you will need to run the `@trigger.dev/cli dev` command to connect to your Trigger.dev instance and allow it to tunnel to your local Next.js server:
```sh
npx @trigger.dev/cli@latest dev
```
At this point you should be able to navigate to your Trigger.dev dashboard and view your registered jobs
@@ -0,0 +1,25 @@
---
title: "Testing Jobs"
description: "You can test Jobs from the dashboard."
---
<Accordion title="Finding the Test page">
There's a tab on the Job page called **Test**. Or you can click the "Test" button in the top right of the page.
![Navigating to the Test page](/images/test-click.png)
</Accordion>
## Your options when Testing
![Your options on the Test page](/images/test-annotated.png)
1. Select the environment you'd like the test to run against.
2. Some Triggers provide example payloads that you can select from. This will populate the code editor below.
3. When you're happy with the payload, click **Run test**.
## Identifying test runs
Tests have a label in the top-right of the Run page and a tick in the Test column of the Runs list.
![The test indicator on the Run page](/images/test-indicator.png)
@@ -0,0 +1,41 @@
---
title: "Local platform"
description: "Running the Trigger.dev platform locally requires tunneling to the internet."
---
Note this is only relevant to you if you are [running the platform locally](/documentation/guides/self-hosting).
## How do I do it?
There are a few ways to do this, but we recommend using [ngrok](https://ngrok.com/). It's free and easy to use.
### Start ngrok
1. Install ngrok:
<CodeGroup>
```sh Mac
brew install ngrok/ngrok/ngrok
```
```sh Windows
choco install ngrok
```
</CodeGroup>
2. Open a new terminal window/tab, you need to leave this running
3. Create an http tunnel at port 3030:
```sh
ngrok http 3030
```
4. Grab your forwarding address in the ngrok output:
![ngrok](/images/ngrok.png)
### 2. Use the forwarding URL
Use the forwarding URL that ngrok gave you for the `LOGIN_ORIGIN` and `API_ORIGIN` environment variables for the docker container.
@@ -0,0 +1,96 @@
---
title: "API Keys and Personal Access Tokens"
description: "Lots of APIs use API Keys or Personal Access Tokens to authenticate. This guide will show you how to use them."
---
## 1. Create an Integration client
```ts
//1. Import the Integration packages you want to use
import { Github } from "@trigger.dev/github";
import { OpenAI } from "@trigger.dev/openai";
//2. Create a new instance of the Integration client
// GitHub uses personal access tokens so the param is called `token`
const github = new Github({
//this is used to identify the connection
id: "github",
token: process.env.GITHUB_TOKEN!,
});
// OpenAI uses API keys so the param is called `apiKey`
const openai = new OpenAI({
//this is used to identify the connection
id: "openai",
apiKey: process.env.OPENAI_API_KEY!,
});
```
Note that we used `process.env.GITHUB_TOKEN` and `process.env.OPENAI_API_KEY`. They're environment variables, which are a way of not putting secret values directly in your code.
For Next.js, local environment variables usually live in a `.env.local` file in the root of the project.
```bash .env.local
#...other variables above here
GITHUB_TOKEN="github_pat_1234567890xxxxxxxxxxxxxxxxxxxx"
OPENAI_API_KEY="sk-12345678xxxxxxxxxxxxxxxxxxxxxxxx"
```
## 2. Use the Integration client
There are two way to use Integrations in a Job:
- You can perform Tasks in the `run` function, by passing the Integration client into the `integrations` object.
- You can use webhooks to Trigger a Job.
This example automatically assigns "matt-aitken" to any new issue in the `trigger.dev` repo (lucky him).
```ts
new Job(client, {
id: "assign-on-issue-opened",
name: "Assign on Issue Opened",
version: "0.1.0",
//1. If you want to use the Integration in the run function, add it here
integrations: { github },
//2. If the Integration supports webhooks, you can use them here
trigger: github.triggers.repo({
event: events.onIssueOpened,
owner: "triggerdotdev",
repo: "trigger.dev",
}),
run: async (payload, io, ctx) => {
//3. Because we add `github` to Integrations, it comes through to `io.github` here
const assignee = await io.github.addIssueAssignees("add assignee", {
owner: payload.repository.owner.login,
repo: payload.repository.name,
issueNumber: payload.issue.number,
assignees: ["matt-aitken"],
});
return assignee;
},
});
```
## 3. Viewing connections in the dashboard
### The Integrations page
<Snippet file="integration-list.mdx" />
### Connected API Key Integration detail page
![An API Key Integration](/images/integration-api-key.png)
Some useful things on this page
1. The `id` that you gave the connection when you defined it in your code
2. The Jobs that are using this connection
## References
<CardGroup cols={2}>
<Card title="View Integrations" icon="grid-2" href="/integrations">
Trigger.dev integrates with a wide range of services.
</Card>
</CardGroup>
@@ -0,0 +1,101 @@
---
title: "OAuth"
description: "Use OAuth to authenticate your team or to add Integrations to your product so your users can connect their accounts."
---
## 1. Create an Integration client (in code)
```ts
//1. Import the Integration packages you want to use
import { Slack } from "@trigger.dev/slack";
import { Github } from "@trigger.dev/github";
//2. Create a new instance of the Integration client
const slack = new Slack({
//this is used to identify the connection
id: "slack",
});
//GitHub supports OAuth (as well as Personal Access Tokens)
const github = new Github({
//this is used to identify the connection
id: "github",
});
```
When using OAuth, you only need to specify the `id`.
## 2. Use the Integration client
There are two way to use Integrations in a Job:
- You can perform Tasks in the `run` function, by passing the Integration client into the `integrations` object.
- You can use webhooks to Trigger a Job.
This example send a Slack message when someone stars the `trigger.dev` GitHub repo 🤩.
```ts
new Job(client, {
id: "star-slack-notification",
name: "New Star Slack Notification",
version: "0.1.0",
//1. If you want to use the Integration in the run function, add it here
integrations: { slack },
//2. If the Integration supports webhooks, you can use them here
trigger: github.triggers.repo({
event: events.onNewStar,
owner: "triggerdotdev",
repo: "empty",
}),
run: async (payload, io, ctx) => {
//3. Because we add `slack` to Integrations, it comes through to `io.slack` here
const response = await io.slack.postMessage("Slack star", {
text: `${payload.sender.login} starred ${payload.repository.full_name}.\nTotal: ${payload.repository.stargazers_count}⭐️`,
channel: "C04GWUTDC3W",
});
},
});
```
## 3. Creating the OAuth connection in the dashboard
Now you need to setup the OAuth client in the dashboard. You will use the `id` that you specified in your code to associate them.
1. Go to the "Integrations page" in your dashboard. Click the Integration you want to setup.
![Click the Integration](/images/integrations-click-github.png)
2. Select OAuth as the authentication type.
![Select OAuth](/images/oauth-setup-1.png)
3. Select "Developer" as the user type.
![Select Developer](/images/oauth-setup-2.png)
4. Enter the `id` that you specified in your code. Select the scopes that you want and enter a unique title.
![Enter the id](/images/oauth-setup-3.png)
5. Click the Connect button, it will take you through the OAuth flow.
## 4. Viewing your connection clients
### The Integrations page
<Snippet file="integration-list.mdx" />
### Connected OAuth integration detail page
![An OAuth Integration](/images/integration-oauth.png)
Some useful things on this page
1. The `id` that you gave the connection when you defined it in your code
2. The Jobs that are using this connection
3. All the connections associated with this Integration client (there will be more than one if you're authenticated your users)
4. The scopes that this connection has access to
## References
<CardGroup cols={2}>
<Card title="View Integrations" icon="grid-2" href="/integrations">
Trigger.dev integrates with a wide range of services.
</Card>
</CardGroup>
@@ -0,0 +1,167 @@
---
title: "Using Integrations"
description: "How to use Integrations"
---
<Note>
You can use any API in your Jobs by using existing Node.js SDKs or HTTP
requests. Integrations just make it much easier especially when you want to
use OAuth. And you get great logging.
</Note>
[Integrations](/documentation/concepts/integrations) allow you to quickly use APIs, including webhooks and Tasks.
## Authentication
There are two ways to authenticate Integrations, OAuth and API Keys/Access Tokens.
<CardGroup cols={2}>
<Card
title="API Keys/Access Tokens"
icon="key"
href="/documentation/guides/using-integrations-apikeys"
>
Use API Keys or Access Tokens to connect an Integration
</Card>
<Card
title="OAuth"
icon={
<svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M50.5013 0C49.1646 0 47.7861 0.0417711 46.3659 0.125313C43.609 0.292398 40.685 0.75188 37.594 1.50376C34.67 2.25564 32.1637 3.09106 30.0752 4.01002C26.9006 5.43024 23.6842 7.26817 20.4261 9.52381C16.7502 12.1136 13.4921 15.2464 10.6516 18.9223C7.81119 22.5982 5.51378 26.6082 3.7594 30.9524L3.13283 32.4561C2.38095 34.2941 1.8797 35.6725 1.62907 36.5915C0.543024 40.3509 0 44.4444 0 48.8722C0 52.9657 0.37594 56.8505 1.12782 60.5263C2.96575 68.8805 6.39098 76.0234 11.4035 81.9549C15.7477 87.1345 21.5539 91.6458 28.8221 95.4887C32.8321 97.5773 37.406 98.9766 42.5439 99.6867C47.6817 100.397 52.8822 100.355 58.1454 99.5614C63.4085 98.7677 68.2122 97.2849 72.5564 95.1128C80.0752 91.2698 86.4244 85.4636 91.604 77.6942C94.1938 73.8513 96.1988 69.6742 97.619 65.1629C99.2063 60.1504 100 54.9708 100 49.6241C100 44.8621 99.1228 39.8914 97.3684 34.7118C95.9482 30.3676 94.0267 26.3158 91.604 22.5564C87.3434 15.7895 81.9967 10.4845 75.5639 6.6416C68.2122 2.21387 59.858 0 50.5013 0ZM46.2406 23.8095H53.0075C54.3442 23.8095 55.5556 24.2063 56.6416 25C57.7277 25.7936 58.4795 26.817 58.8972 28.0702L72.1805 68.1704C72.7652 69.8413 72.6608 71.4494 71.8672 72.995C71.0735 74.5405 69.883 75.5639 68.2957 76.0652C67.6274 76.2322 66.9591 76.3158 66.2907 76.3158C64.9541 76.3158 63.7427 75.9398 62.6566 75.188C61.5706 74.4361 60.8187 73.3918 60.401 72.0551L57.5188 62.9073H42.4812L39.599 72.0551C39.1813 73.3083 38.4294 74.3317 37.3434 75.1253C36.2573 75.919 35.0459 76.3158 33.7093 76.3158C33.0409 76.3158 32.4144 76.2322 31.8296 76.0652C30.1587 75.5639 28.9265 74.5614 28.1328 73.0576C27.3392 71.5539 27.193 69.9666 27.6942 68.2957L40.3509 28.1955C40.7686 26.8588 41.5205 25.7936 42.6065 25C43.6926 24.2063 44.9039 23.8095 46.2406 23.8095Z"
fill="#818cf8"
/>
</svg>
}
href="/documentation/guides/using-integrations-oauth"
>
Use OAuth to connect an Integration for your team or your users
</Card>
</CardGroup>
## Using for Jobs & Tasks
You must pass Integrations into your Job to run integration tasks. Passed in Integrations will be available on the `io` object in the `run()` function with the same name as the key. For example:
<Snippet file="how-to-pass-integrations.mdx" />
Both the `postMessage` and `addIssueLabels` functions above are implemented as an "Authenticated Task" that is defined inside the Integration, where the first argument is always the task [key](/documentation/concepts/tasks#task-keys) and the second argument is the parameters for the task. For example, here is how you would call the `postMessage` function above:
```ts
client.defineJob({
// ...
integrations: {
slack,
},
run: async (payload, io, ctx) => {
const response = await io.slack.postMessage("✋", {
channel: "C04GWUTDC3W",
text: "My first Slack message",
});
},
});
```
The above code will create a [Task](/documentation/concepts/tasks) that will use the authentication configured for the specified Slack integration.
If the integration does not support a specific task function that you want to use, you can use the `runTask` function to get access to the authenticated client.
For example, the Slack integration doesn't expose an Authenticated Task function for deleting a message, but you can use the `chat.delete` function provided by the [@slack/web-api package](https://slack.dev/node-slack-sdk/web-api) directly:
```ts
client.defineJob({
// ...
integrations: {
slack,
},
run: async (payload, io, ctx) => {
const response = await io.slack.postMessage("✋", {
channel: "C04GWUTDC3W",
text: "My first Slack message",
});
await io.wait("⏰", 24 * 60 * 60 * 1000); // wait 24 hours
await io.slack.runTask("delete", async (slack) => {
return slack.chat.delete({
channel: "C04GWUTDC3W",
ts: response.ts!,
});
});
},
});
```
You can optionally pass a third parameter to `runTask` to specify the task parameters:
```ts
await io.slack.runTask(
"delete",
async (slack) => {
return slack.chat.delete({
channel: "C04GWUTDC3W",
ts: response.ts!,
});
},
{
name: "Delete message",
properties: [{ label: "Message", text: response.ts }],
}
);
```
View the [runTask](/sdk/io/runtask#parameters) reference for more information.
## Using outside of Jobs
If you want to use integrations that support "local auth" only (e.g. API keys, like Stripe and Supabase) outside of a Job you can use the `.native` property on the integration to get direct access to the client. For example:
```ts stripe.ts
import { Stripe } from "@trigger.dev/stripe";
const stripe = new Stripe({
id: "stripe",
apiKey: process.env.STRIPE_SECRET_KEY!
});
async function createCustomer() {
await stripe.native.customers.create({
// ... customer attributes go here
});
}
```
## Using for Triggers
Some integrations provide triggers that you can use to start a Job. For example, the GitHub integration provides a `push` trigger that will start a Job when a new commit is pushed to a repository:
```ts
import { Github, events } from "@trigger.dev/github";
const github = new Github({
id: "github",
token: process.env.GITHUB_TOKEN!,
});
client.defineJob({
id: "github-integration-on-push",
name: "GitHub Integration - On Push",
version: "0.1.0",
trigger: github.triggers.repo({
event: events.onPush,
owner: "triggerdotdev",
repo: "trigger.dev",
}),
run: async (payload, io, ctx) => {
// do something on push
},
});
```
Behind the scenes, our `@trigger.dev/github` integration will create a webhook on your repository that will call our API when a new push event is received. We will then start your Job with the payload from the push event.
<Note>
If you are just using an integration to trigger a job but not using
authenticated tasks inside the job run, there is no need to pass the
integration in the job `integrations` option.
</Note>
@@ -0,0 +1,28 @@
---
title: "Video walkthrough"
description: "Go from zero to a working Job in your Next.js app in 10 minutes."
---
The Job subscribes to new GitHub issues and if the issues haven't been dealt with after 24 hours a Slack reminder is sent and they're assigned to someone.
You can view the [source code here](https://github.com/triggerdotdev/examples/tree/main/github-issue-reminder).
<iframe
className="w-full aspect-video"
src="https://www.youtube.com/embed/uocBQt2HeQo"
title="Create a serverless background job in 10 mins"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
We cover how to:
- get around serverless timeouts using Trigger.dev.
- use the CLI to get setup.
- run the Job locally.
- create a GitHub onIssueOpened Trigger.
- test the Job.
- use Integrations inside the Job, including using API Keys (GitHub) and OAuth (Slack).
- write the main logic of the Job.
- use rerun to quickly iterate.
@@ -0,0 +1,20 @@
---
title: "Viewing Runs"
description: "See every Task in every Run so you can tell exactly what happened."
---
## The Runs list
The main page for a Job is a paginated table of Runs. The newest Runs are at the top.
![Runs list](/images/runs-list.png)
<Note>Filtering Runs is coming soon</Note>
## Run page
![Run Page](/images/run-page.png)
You can view the details of the Trigger, all the Tasks and the return value of the Run by clicking the elements in the timeline of the left hand side.
Some Tasks have subtasks which are collapsed by default but can be opened by clicking them.
+308
View File
@@ -0,0 +1,308 @@
---
title: "Zod Guide"
sidebarTitle: "Zod"
description: "TypeScript-first schema validation with static type inference"
---
## Intro
Zod is a fantastic utility package by [@colinhacks](https://twitter.com/colinhacks) that allows for defining runtime schema validation and type-safety.
We use it [extensively](https://github.com/search?q=repo%3Atriggerdotdev%2Ftrigger.dev+%22zod%22%3B&type=code) internally at Trigger.dev.
But there are a few places where we ask you to provide us with a Zod schema, for example when defining your own [events](/documentation/concepts/triggers/events):
```ts
new Job(client, {
id: "new-user",
name: "New user",
version: "0.1.0",
//the eventTrigger uses zod to define the schema
trigger: eventTrigger({
name: "user.created",
schema: z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
}),
}),
//this function is run when the custom event is received
run: async (payload, io, ctx) => {
//do stuff
},
});
```
So it will help to know a little about Zod and how to use it. We definitely recommend the well written [Zod README](https://github.com/colinhacks/zod#readme) but we've included a short primer below.
<Tip>
Wherever we require you to pass in a Zod schema, you can always start with
`z.any()` which accepts `any` type and then add more strict validations later.
</Tip>
## Basic Usage
There are three main steps to using Zod:
1. Define a schema
```ts
import { z } from "zod";
const mySchema = z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
});
```
2. Infer TypeScript types from the schema
```ts
type MySchema = z.infer<typeof mySchema>;
```
3. Validate data against the schema
```ts
const data: unknown = {
name: "Eric",
email: "eric@trigger.dev",
paidPlan: true,
};
const result = mySchema.parse(data);
```
When using Zod with Trigger.dev, you'll only really need to do the first step, and by passing it to us (through the `schema` property of the `customEvent` function), we'll do the second and third steps for you.
## Defining Schemas
### Primitives
Zod schemas are a way to define the shape of an object. They can be as simple as a single type, or as complex as a nested object.
```ts
// Primitives
z.string();
z.number();
z.boolean();
z.date();
z.undefined();
z.null();
```
Any schema can be marked as optional, which means the schema can be `undefined` or `null`:
```ts
z.string().optional();
```
Schemas can also be marked optional by providing a default value:
```ts
const optionalString = z.string().default("default value");
const value = optionalString.parse(undefined); // value === "default value"
```
If you need to allow a value to be `null`, you can use `nullable()`:
```ts
const nullableString = z.string().nullable();
const value = nullableString.parse(null);
```
You can also use Zod to coerce primites into other types. For example, you can coerce a string into a number:
```ts
const numberString = z.coerce.number();
const value = numberString.parse("123"); // value === 123
```
The following primitives are supported:
```ts
z.coerce.string();
z.coerce.number();
z.coerce.boolean();
z.coerce.bigint();
z.coerce.date();
```
Coercing dates are especially useful when you are receiving a `string` from an API and want to convert it to a JavaScript `Date` object:
```ts
const date = z.coerce.date();
const value = date.parse("2021-01-01T00:00:00.000Z"); // value === Date object
```
### Objects
Object schemas are the most common type of schema. They allow you to define the shape of an object, and the types of each property.
```ts
const mySchema = z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
});
```
All properties are required by default, although you can make them all optional using `partial()`:
```ts
const mySchema = z
.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
})
.partial();
```
You can also make individual properties optional:
```ts
const mySchema = z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean().optional(),
});
```
By default an object schema will strip out any extra properties that are not defined in the schema. You can disable this behavior using `passthrough()`:
```ts
const mySchema = z.object({
name: z.string(),
});
mySchema.parse({ name: "Eric", email: "eric@trigger.dev" }); // { name: "Eric" }
mySchema.passthrough().parse({ name: "Eric", email: "eric@trigger.dev" }); // { name: "Eric", email: "eric@trigger.dev" }
```
Or you can use `strict()` to make the schema throw an error if there are any extra properties:
```ts
const mySchema = z
.object({
name: z.string(),
})
.strict();
mySchema.parse({ name: "Eric", email: "eric@trigger.dev" }); // throws error
```
Zod includes a few useful object schema utilities to help with reusing schemas, `extends()` and `merge()`:
```ts
const baseSchema = z.object({
name: z.string(),
});
const extendedSchema = baseSchema.extend({
email: z.string(),
});
const mergedSchema = baseSchema.merge(
z.object({
email: z.string(),
})
);
```
You can also use `pick()` and `omit()` to create a new schema that only includes or excludes certain properties:
```ts
const mySchema = z.object({
name: z.string(),
email: z.string(),
paidPlan: z.boolean(),
});
const pickedSchema = mySchema.pick({ name: true, email: true });
const omittedSchema = mySchema.omit({ paidPlan: true });
```
### Arrays
You can specify the schema of an array using `z.array()`:
```ts
z.array(z.string()); // string[]
z.array(z.number()); // number[]
z.array(z.object({ name: z.string() })); // Array<{ name: string }>
```
You can also specify the type of array that has a fixed number of elements using `z.tuple()`:
```ts
z.tuple([z.string(), z.number(), z.boolean()]); // [string, number, boolean]
```
### Unions
You can specify a union of schemas using `z.union()`:
```ts
z.union([z.string(), z.number(), z.boolean()]); // string | number | boolean
```
Discriminating unions are also supported, and especially useful when paired with type narrowing:
```ts
const mySchema = z.discriminatingUnion("type", [
z.object({
type: z.literal("a"),
data: z.string(),
}),
z.object({
type: z.literal("b"),
data: z.number(),
}),
]);
const value = mySchema.parse({ type: "a", data: "hello" });
if (type.a) {
// value is { type: "a", data: string }
} else if (type.b) {
// value is { type: "b", data: number }
}
```
### Records
You can specify a record of schemas using `z.record()`, useful for when you have a map of values but don't care about the keys:
```ts
z.record(z.string()); // Record<string, string>
z.record(z.number()); // Record<string, number>
```
### JSON type
If you want to accept any valid JSON value, you can use the following schema:
```ts
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
type Literal = z.infer<typeof literalSchema>;
type Json = Literal | { [key: string]: Json } | Json[];
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])
);
jsonSchema.parse(data);
```
Hat tip to [ggoodman](https://github.com/ggoodman) for this one.
## Resources
Matt Pocock [@mattpocock](https://twitter.com/mattpocockuk) has a great free Zod Tutorial up on [Total Typescript](https://www.totaltypescript.com/tutorials/zod) that covers the basics of Zod.
Easily generate Zod schemas from [JSON](https://transform.tools/json-to-zod), [JSON Schemas](https://transform.tools/json-schema-to-zod), or [TypeScript](https://transform.tools/typescript-to-zod) types.
+81
View File
@@ -0,0 +1,81 @@
---
title: Introduction
description: "Welcome to the Trigger.dev documentation."
---
Trigger.dev is an open source framework for creating long-running Jobs directly in your Next.js app with API Integrations, webhooks, scheduling and delays. You can reliably run Jobs that wouldn't normally work in serverless environments (like Vercel) because of timeouts.
You can use [Trigger.dev Cloud](https://cloud.trigger.dev) or [Self-host Trigger.dev](/documentation/guides/self-hosting) on your own infrastructure.
<Note>
Trigger.dev v2 currently only supports serverless. During our beta we will add
[support for long-running
servers](https://github.com/triggerdotdev/trigger.dev/issues/244).
</Note>
<CardGroup>
<Card title="Quick start" icon="person-running-fast" href="quickstart">
Get started in 5 minutes.
</Card>
<Card
title="Video walkthrough"
icon="video"
href="/documentation/guides/video-walkthrough"
>
Go from zero to a working Job in your Next.js app in 10 minutes.
</Card>
<Card
title="What is Trigger.dev"
icon="wand-magic-sparkles"
href="/documentation/concepts/what-is-triggerdotdev"
>
Learn more about how Trigger.dev works and how it can help you.
</Card>
<Card title="Integrations" icon="grid-2" href="/integrations">
Trigger.dev integrates with a wide range of services.
</Card>
<Card title="Examples" icon="slot-machine" href="/examples">
One of the quickest ways to learn how Trigger.dev works is to view some
example Jobs.
</Card>
</CardGroup>
## Getting help
We'd love to hear from you or give you a hand getting started. Here are some ways to get in touch with us. We'd also ❤️ your support.
<CardGroup>
<Card
title="Join our Discord server"
icon="discord"
href="https://discord.gg/kA47vcd8P6"
color="#5865F2"
>
The #help-and-questions channel is a great place to get help with any
questions about Trigger.dev.
</Card>
<Card
title="Follow us on Twitter"
icon="twitter"
href="https://twitter.com/triggerdotdev"
color="#1DA1F2"
>
Follow us on Twitter to get the latest updates and news.
</Card>
<Card
title="Schedule a call"
icon="phone"
href="https://cal.com/team/triggerdotdev/support"
>
Arrange a call with one of the founders. We can help answer questions, build
API Integrations for you and give 1-on-1 help building your first Job.
</Card>
<Card
title="Give us a star on GitHub"
icon="star"
href="https://github.com/triggerdotdev/trigger.dev"
color="#fbbf24"
>
Check us out at triggerdotdev/trigger.dev
</Card>
</CardGroup>
+236
View File
@@ -0,0 +1,236 @@
---
title: "Quick Start"
description: "Start creating Jobs in 5 minutes"
---
This quick start guide will get you up and running with Trigger.dev.
<Accordion title="Don't have a Next.js project yet to add Trigger.dev to? No problem, you can complete the Quick Start using a blank Next.js project:">
Create a blank project by running the `create-next-app` command in your terminal:
```bash
npx create-next-app@latest
```
Trigger.dev works with either the Pages or App Router configuration.
</Accordion>
## Create a Trigger.dev account
You can either
- Use the [Trigger.dev Cloud](https://cloud.trigger.dev) (we're currently in private beta).
- Or [self-host](/documentation/guides/self-hosting) the service.
### Create your first project
Once you've created an account, follow the steps to:
1. Complete your account details.
2. Create your first Organization and Project.
### Getting an API key
1. Go to the "Environments & API Keys" page in your project.
![Go to the Environments & API Keys page ](/images/environments-link.png)
2. Copy the `DEV` API key.
![API Keys](/images/api-keys.png)
## Run the CLI `init` command
The easiest way to get started it to use the CLI. It will add Trigger.dev to your existing Next.js project, setup a route and give you an example file.
In a terminal window run:
<CodeGroup>
```bash npm
npx @trigger.dev/cli@latest init
```
```bash pnpm
pnpm dlx @trigger.dev/cli@latest init
```
```bash yarn
yarn dlx @trigger.dev/cli@latest init
```
</CodeGroup>
It will ask you a few questions
1. Are you using the [Trigger.dev Cloud](https://trigger.dev) or [self-hosting](/documentation/guides/self-hosting)? You're probably using the cloud.
2. Enter your development API key. Enter the key you copied earlier.
3. Enter a unique ID for your endpoint (you can just use the default by hitting enter)
## Run your Next.js site
Make sure your Next.js site is running locally, we will connect to it to register your Jobs.
<Warning>You must leave this running for the rest of the steps.</Warning>
<CodeGroup>
```bash npm
npm run dev
```
```bash pnpm
pnpm run dev
```
```bash yarn
yarn run dev
```
</CodeGroup>
## Run the CLI `dev` command
The CLI `dev` command allows the Trigger.dev service to send messages to your Next.js site. This is required for registering Jobs, triggering them and running tasks. To achieve this it creates a tunnel (using [ngrok](https://ngrok.com/)) so Trigger.dev can send messages to your machine.
You should leave the `dev` command running when you're developing.
In a **new terminal window or tab** run:
<CodeGroup>
```bash npm
npx @trigger.dev/cli@latest dev
```
```bash pnpm
pnpm dlx @trigger.dev/cli@latest dev
```
```bash yarn
yarn dlx @trigger.dev/cli@latest dev
```
</CodeGroup>
<br />
<Note>
You can optionally pass the port if you're not running on 3000 by adding
`--port 3001` to the end
</Note>
<AccordionGroup>
<Accordion title="Experiencing an error? This could be due to middleware.">
Instructions of how to resolve any issues due to middleware [here](https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware)
</Accordion>
<Accordion title="Advanced: Run your Next.js server together with the CLI">
You can modify your `package.json` to run both the Next.js server and the CLI `dev` command together.
1. Install the `concurrently` package:
<CodeGroup>
```bash npm
npm install concurrently --save-dev
```
```bash pnpm
pnpm install concurrently --save-dev
```
```bash yarn
yarn add concurrently --dev
```
</CodeGroup>
2. Modify your `package.json` file's `dev` script.
```json package.json
...
"scripts": {
"dev": "concurrently --kill-others npm:dev:*",
"dev:next": "next dev",
"dev:trigger": "npx @trigger.dev/cli dev",
...
}
...
```
</Accordion>
</AccordionGroup>
## Your first job
The CLI init command created a simple Job for you. There will be a new file either `api/trigger/route.ts` or `pages/api/trigger.ts`.
In there is this Job:
```typescript
//Job definition uses the client
new Job(client, {
// 1. Metadata
id: "example-job",
name: "Example Job",
version: "0.0.1",
// 2. Trigger
trigger: eventTrigger({
name: "example.event",
}),
// 3. Run function
run: async (payload, io, ctx) => {
// do something
await io.logger.info("Hello world!", { payload });
return {
message: "Hello world!",
};
},
});
```
If you navigate to your Trigger.dev project you will see this Job in the "Jobs" section:
![Your first Job](/images/first-job.png)
## Triggering the Job
There are two way to trigger this Job.
1. Use the "Test" functionality in the dashboard.
2. Use the Trigger.dev API (either via our SDK or a web request)
### "Testing" from the dashboard
Click into the Job and then open the "Test" tab. You should see this page:
![Test Job](/images/test-job.png)
This Job doesn't have a payload schema (meaning it takes an empty object), so you can simple click the "Run test" button.
Congratulations, you should get redirected so you can see your first Run!
## What's next?
<CardGroup cols={2}>
<Card
title="Write your first Job"
icon="hexagon-plus"
href="/documentation/guides/create-a-job"
>
A Guide for how to create your first real Job
</Card>
<Card
title="What is Trigger.dev"
icon="wand-magic-sparkles"
href="/documentation/concepts/what-is-triggerdotdev"
>
Learn more about how Trigger.dev works and how it can help you.
</Card>
<Card title="Examples" icon="slot-machine" href="/examples">
One of the quickest ways to learn how Trigger.dev works is to view some
example Jobs.
</Card>
<Card title="Get help" icon="hire-a-helper" href="/documentation/get-help">
Struggling getting setup or have a question? We're here to help.
</Card>
</CardGroup>
+15
View File
@@ -0,0 +1,15 @@
---
title: "Roadmap"
description: "Coming changes to Trigger.dev"
---
## Next up
Features and Integrations we'll be working on next.
#### Features
- Trigger.dev Connect: authenticate your users so you can quickly add Integrations to your app.
- Vercel Integration
- Error alerts: get notified when your Jobs fail or don't start.
- Filtering Runs (e.g. by status)
+17
View File
@@ -0,0 +1,17 @@
---
title: Examples Repository
description: "Pre-built Next.js projects with Jobs."
---
We have an [examples GitHub repo](https://github.com/triggerdotdev/examples) that we add to all the time. It's a great place to start if you're looking for inspiration.
Instructions on how to run each of the below projects are included in their respective READMEs (linked below).
| Name | Description | Integrations |
| -------------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------- |
| [Delays](https://github.com/triggerdotdev/examples/tree/main/delays) | A Next.js project containing Jobs using delays | N/A |
| [Scheduled](https://github.com/triggerdotdev/examples/tree/main/scheduled) | A Next.js project containing interval and cron scheduled Jobs | N/A |
| [GitHub](https://github.com/triggerdotdev/examples/tree/main/github) | A Next.js project containing GitHub Jobs | [GitHub](/integrations/apis/github) |
| [OpenAI](https://github.com/triggerdotdev/examples/tree/main/openai) | A Next.js project containing OpenAI Jobs | [OpenAI](/integrations/apis/openai) |
| [Resend](https://github.com/triggerdotdev/examples/tree/main/resend) | A Next.js project containing Resend Jobs | [Resend](/integrations/apis/resend) |
| [Slack](https://github.com/triggerdotdev/examples/tree/main/slack) | A Next.js project containing Slack Jobs | [Slack](integrations/apis/slack) |
+25
View File
@@ -0,0 +1,25 @@
---
title: Example Jobs
description: "An ever-growing list of example Jobs which you can use to get started with Trigger.dev."
---
<Info>
If you are using integrations, you'll need set up authentication either using
OAuth or API keys / access tokens. You can find out how to do that in the
[integrations section](/integrations).
</Info>
| Job (code in link) | Description | Integrations used |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| [Basic delay](https://github.com/triggerdotdev/examples/blob/main/delays/src/jobs/delayJob.ts) | Logs a message to the console, waits for 5 minutes, and then logs another message. | N/A |
| [Basic interval](https://github.com/triggerdotdev/examples/blob/main/scheduled/src/jobs/interval.ts) | This Job will run every 60 seconds, starting 60 seconds after this Job is first indexed. | N/A |
| [Cron scheduled interval](https://github.com/triggerdotdev/examples/blob/main/scheduled/src/jobs/cronScheduled.ts) | A scheduled Job which runs at 2:30pm every Monday. | N/A |
| [Tell me a joke using OpenAI](https://github.com/triggerdotdev/examples/blob/main/openai/src/jobs/tellMeAJoke.ts) | Generates a random joke using OpenAI GPT 3.5. | [OpenAI](/integrations/apis/openai) |
| [Generate an image using OpenAI](https://github.com/triggerdotdev/examples/blob/main/openai/src/jobs/generateHedgehogImages.ts) | Generates a random image of a hedgehog using OpenAI DALL-E. | [OpenAI](/integrations/apis/openai) |
| [Add a custom label to a GitHub issue when it is created](https://github.com/triggerdotdev/examples/blob/main/github/src/jobs/onIssueOpened.ts) | When a new GitHub issue is opened it adds a "Bug" label to it. | [GitHub](/integrations/apis/github) |
| [GitHub new star alert](https://github.com/triggerdotdev/examples/blob/main/github/src/jobs/newStarAlert.ts) | When a repo is starred a message is logged with the new Stargazers count. | [GitHub](/integrations/apis/github) |
| [Github new star alert in Slack](https://github.com/triggerdotdev/examples/blob/main/github/src/jobs/newStarToSlack.ts) | When a repo is starred, a message is sent to a Slack channel with the name and URL of the GitHub user who starred the repo, and the updated Stargazers count. | [GitHub](/integrations/apis/github), [Slack](/integrations/apis/slack) |
| [Send a Slack message when an event is received](https://github.com/triggerdotdev/examples/blob/main/slack/src/jobs/sendSlackMessage.ts) | Sends a Slack message to a specific channel when an event is received. | [Slack](/integrations/apis/slack) |
| [Send an email using Resend](https://github.com/triggerdotdev/examples/blob/main/resend/src/jobs/resendBasicEmail.ts) | Send a basic email using Resend | [Resend](/integrations/apis/resend) |
If you have any ideas for Jobs you would like to build, you can fill in the [Job request form](https://bcymafitv0e.typeform.com/to/YLUKy9my#source=example-jobs-docs).
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Some files were not shown because too many files have changed in this diff Show More