Full Next.js guide (#1259)

* tasks no longer inside a group in the side menu (and added “cron”)

* Delay using a timezone

* Added React Not Defined error to the troubleshooting page

* Improved the React common problem

* Link to v2 docs

* New Development section and entry in Common Problems

* Concurrently running the terminal

* Fixed the .env weirdness

* Added section on creating PATs for Github actions

* Improved the Machine spec and limits page

* Quick start steps now have nice images

* Added a diagram for the lifecycle functions

* Added note about onFailure

* CRON -> cron/Cron

* WIP adding more steps to the next.js guide

* WIP next.js

* WIP adding tabbed steps for pages/app router

* References to Infisical links to their homepage so it’s clearer

* WIP updating the nextjs guide

* WIP nextjs guide

* More nextjs guide steps

* More copy

* Added rate limit trouble shooting

* Removed old prisma error title

* Added secret key step

* Added a note for logging in using a specified domain if self hosting

* typo

* App router docs copy

* Deploy copy update

* Added a favicon.png to fix a docs build error

* Removed unused snippet

* Server actions now inside a tab

* Server actions + restructured the triggering section

* Added troubleshooting snippet for react event handlers

* Added a new troubleshooting snippet for ESM

* Updated old replaying image to reflect the new UI

* Removed references to reattempting

* Updated replaying from the run page

* Added a bulk replay section

* Updated the writing tasks intro page

* Removed edge runtime code for now

* Added edge runtime – it seems to just work!

* import type
This commit is contained in:
James Ritchie
2024-08-20 14:50:39 +01:00
committed by GitHub
parent 9d529e9f17
commit d6786002b2
21 changed files with 508 additions and 102 deletions
+3 -27
View File
@@ -51,32 +51,12 @@ yarn dlx trigger.dev@beta dev --debugger
### Concurrently running the terminal
The concurrently package allows you to run multiple package.json scripts at the same time in this case, your site and Trigger.dev.
Install the concurrently package as a dev dependency:
<Steps>
<Step title="Install the concurrently package as a dev dependency">
<CodeGroup>
```bash npm
npm i concurrently -D
```ts
concurrently --raw --kill-others npm:dev:remix npm:dev:trigger
```
```bash pnpm
pnpm i concurrently -D
```
```bash yarn
yarn i concurrently -D
```
</CodeGroup>
</Step>
<Step title="Update your package.json">
Then add something like this in your package.json scripts. This assumes you're running Next.js so swap that bit out if you're not:
```json
@@ -87,7 +67,3 @@ Then add something like this in your package.json scripts. This assumes you're r
//...
}
```
</Step>
</Steps>
+4 -4
View File
@@ -87,7 +87,7 @@ We have a complete set of SDK functions (and REST API) you can use to directly m
You could use the SDK functions above but it's much easier to use our `resolveEnvVars` function in your `trigger.config` file.
In this example we're using env vars from Infisical.
In this example we're using env vars from [Infisical](https://infisical.com).
```ts /trigger.config.ts
import type { TriggerConfig, ResolveEnvironmentVariablesFunction } from "@trigger.dev/sdk/v3";
@@ -136,13 +136,13 @@ export const config: TriggerConfig = {
#### Local development
When you [develop locally](/cli-dev) `resolveEnvVars()` will inject the env vars from Infisical into your local `process.env`.
When you [develop locally](/cli-dev) `resolveEnvVars()` will inject the env vars from [Infisical](https://infisical.com) into your local `process.env`.
#### Deploy
When you run the [CLI deploy command](/cli-deploy) directly or using [GitHub Actions](/github-actions) it will sync the environment variables from Infisical to Trigger.dev. This means they'll appear on the Environment Variables page so you can confirm that it's worked.
When you run the [CLI deploy command](/cli-deploy) directly or using [GitHub Actions](/github-actions) it will sync the environment variables from [Infisical](https://infisical.com) to Trigger.dev. This means they'll appear on the Environment Variables page so you can confirm that it's worked.
This means that you need to redeploy your Trigger.dev tasks if you change the environment variables in Infisical.
This means that you need to redeploy your Trigger.dev tasks if you change the environment variables in [Infisical](https://infisical.com).
### The variables return type
+278 -5
View File
@@ -11,18 +11,291 @@ import CliDevStep from '/snippets/step-cli-dev.mdx';
import CliRunTestStep from '/snippets/step-run-test.mdx';
import CliViewRunStep from '/snippets/step-view-run.mdx';
import UsefulNextSteps from '/snippets/useful-next-steps.mdx';
import TriggerTaskNextjs from '/snippets/trigger-tasks-nextjs.mdx';
import NextjsTroubleshootingMissingApiKey from '/snippets/nextjs-missing-api-key.mdx';
import NextjsTroubleshootingButtonSyntax from '/snippets/nextjs-button-syntax.mdx';
import WorkerFailedToStartWhenRunningDevCommand from '/snippets/worker-failed-to-start.mdx';
<Note>The following guide should work for both App and Pages router</Note>
<Note>This guide can be followed for both App and Pages router as well as Server Actions.</Note>
<Prerequisites framework="Next.js" />
## Initial setup
<Steps>
<CliInitStep />
<CliDevStep />
<CliRunTestStep />
<CliViewRunStep />
<CliInitStep />
<CliDevStep />
<CliRunTestStep />
<CliViewRunStep />
</Steps>
## Set your secret key locally
Set your `TRIGGER_SECRET_KEY` environment variable in your `.env.local` file if using the Next.js App router or `.env` file if using Pages router. This key is used to authenticate with Trigger.dev, so you can trigger runs from your Next.js app. Visit the API Keys page in the dashboard and select the DEV secret key.
![How to find your secret key](/images/api-keys.png)
For more information on authenticating with Trigger.dev, see the [API keys page](/apikeys).
## Triggering your task in Next.js
Here are the steps to trigger your task in the Next.js App and Pages router and Server Actions.
<Tabs>
<Tab title="App Router">
<Steps>
<Step title="Create a Route Handler">
Add a Route Handler by creating a `route.ts` file (or `route.js` file) in the `app/api` directory like this: `app/api/hello-world/route.ts`.
</Step>
<Step title="Add your task">
Add this code to your `route.ts` file which imports your task along with `NextResponse` to handle the API route response:
```ts app/api/hello-world/route.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { helloWorldTask } from "@/trigger/example";
import { tasks } from "@trigger.dev/sdk/v3";
import { NextResponse } from "next/server";
//tasks.trigger also works with the edge runtime
//export const runtime = "edge";
export async function GET(request: Request) {
const handle = await tasks.trigger<typeof helloWorldTask>(
"hello-world",
"James"
);
return NextResponse.json(handle);
}
```
</Step>
<Step title="Trigger your task">
<TriggerTaskNextjs/>
</Step>
</Steps>
</Tab>
<Tab title="App Router (Server Actions)">
<Steps>
<Step title="Create an `actions.ts` file">
Create an `actions.ts` file in the `app/api` directory and add this code which imports your `helloWorldTask()` task. Make sure to include `"use server";` at the top of the file.
```ts app/api/actions.ts
"use server";
import type { helloWorldTask } from "@/trigger/example";
import { tasks } from "@trigger.dev/sdk/v3";
export async function myTask() {
try {
const handle = await tasks.trigger<typeof helloWorldTask>(
"hello-world",
"James"
);
return { handle };
} catch (error) {
console.error(error);
return {
error: "something went wrong",
};
}
}
```
</Step>
<Step title="Create a button to trigger your task">
For the purposes of this guide, we'll create a button with an `onClick` event that triggers your task. We'll add this to the `page.tsx` file so we can trigger the task by clicking the button. Make sure to import your task and include `"use client";` at the top of your file.
```ts app/page.tsx
"use client";
import { myTask } from "./actions";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<button
onClick={async () => {
await myTask();
}}
>
Trigger my task
</button>
</main>
);
}
```
</Step>
<Step title="Trigger your task">
Run your Next.js app:
<CodeGroup>
```bash npm
npm run dev
```
```bash pnpm
pnpm run dev
```
```bash yarn
yarn dev
```
</CodeGroup>
Open your app in a browser, making sure the port number is the same as the one you're running your Next.js app on. For example, if you're running your Next.js app on port 3000, visit:
```bash
http://localhost:3000
```
Run the dev server from Step 2. of the [Initial Setup](/guides/frameworks/nextjs#initial-setup) section above if it's not already running:
<CodeGroup>
```bash npm
npx trigger.dev@beta dev
```
```bash pnpm
pnpm dlx trigger.dev@beta dev
```
```bash yarn
yarn dlx trigger.dev@beta dev
```
</CodeGroup>
Then click the button we created in your app to trigger the task. You should see the CLI log the task run with a link to view the logs.
![Trigger.dev CLI showing a successful run](/images/trigger-cli-run-success.png)
Visit the [Trigger.dev dashboard](https://cloud.trigger.dev) to see your run.
</Step>
</Steps>
</Tab>
<Tab title="Pages Router">
<Steps>
<Step title="Create an API route">
Create an API route in the `pages/api` directory. Then create a `hello-world .ts` (or `hello-world.js`) file for your task and copy this code example:
```ts pages/api/hello-world.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import { helloWorldTask } from "@/trigger/example";
import { tasks } from "@trigger.dev/sdk/v3";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<{ id: string }>
) {
const handle = await tasks.trigger<typeof helloWorldTask>(
"hello-world",
"James"
);
res.status(200).json(handle);
}
```
</Step>
<Step title="Trigger your task">
<TriggerTaskNextjs/>
</Step>
</Steps>
</Tab>
</Tabs>
## Add your environment variables (optional)
If you have any environment variables in your tasks, be sure to add them in the dashboard so deployed code runs successfully. In Node.js, these environment variables are accessed in your code using `process.env.MY_ENV_VAR`.
In the sidebar select the "Environment Variables" page, then press the "New environment variable"
button. ![Environment variables page](/images/environment-variables-page.jpg)
You can add values for your local dev environment, staging and prod. ![Environment variables
page](/images/environment-variables-panel.jpg)
You can also add environment variables in code by following the steps on the [Environment Variables page](/deploy-environment-variables#in-your-code).
## Deploying your task in Next.js
For this guide, we'll manually deploy your task by running the [CLI deploy command](/cli-deploy) below. Other ways to deploy are listed in the next section.
<CodeGroup>
```bash npm
npx trigger.dev@beta deploy
```
```bash pnpm
pnpm dlx trigger.dev@beta deploy
```
```bash yarn
yarn dlx trigger.dev@beta deploy
```
</CodeGroup>
### Other ways to deploy
<Tabs>
<Tab title="GitHub Actions">
Use GitHub Actions to automatically deploy your tasks whenever new code is pushed and when the `trigger` directory has changes in it. Follow [this guide](/github-actions) to set up GitHub Actions.
</Tab>
<Tab title="Vercel Integration">
We're working on adding an official [Vercel integration](/vercel-integration) which you can follow the progress of [here](https://feedback.trigger.dev/p/vercel-integration-3).
</Tab>
</Tabs>
## Troubleshooting
<NextjsTroubleshootingMissingApiKey/>
<NextjsTroubleshootingButtonSyntax/>
<WorkerFailedToStartWhenRunningDevCommand/>
<UsefulNextSteps />
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

+2 -1
View File
@@ -4,6 +4,7 @@ description: "There are some hard and soft limits in v3 that you might hit."
---
import SoftLimit from '/snippets/soft-limit.mdx';
import RateLimitHitUseBatchTrigger from '/snippets/rate-limit-hit-use-batchtrigger.mdx';
## Concurrency limits
@@ -24,7 +25,7 @@ These are the default limits on a free account.
Generally speaking each SDK call is an API call.
The most common cause of hitting the API rate limit is if you're calling `trigger()` on a task in a loop, instead of doing this use `batchTrigger()` which will trigger multiple tasks in a single API call. You can have up to 100 tasks in a single batch trigger call.
<RateLimitHitUseBatchTrigger/>
## Schedules
+5 -1
View File
@@ -67,6 +67,10 @@
{
"source": "/v3/:slug*",
"destination": "/:slug*"
},
{
"source": "/reattempting-replaying",
"destination": "/replaying"
}
],
@@ -124,7 +128,7 @@
"versioning",
"machines",
"idempotency",
"reattempting-replaying",
"replaying",
"tags",
"notifications",
"run-usage"
+8
View File
@@ -277,3 +277,11 @@ By default, the Trigger.dev webapp sends telemetry data to our servers. This dat
```bash
TRIGGER_TELEMETRY_DISABLED=1
```
## Login via the CLI
To avoid being redirected to the Cloud login page when using the CLI, you can specify the URL of your self-hosted instance with the `-a` flag. For example:
```
npx trigger.dev@beta login -a http://example.com
```
-57
View File
@@ -1,57 +0,0 @@
---
title: "Reattempting & Replaying"
description: "You can reattempt a task that has failed all of its attempts. You can also replay a task with a new version of your code."
---
import ComingSoon from "/snippets/coming-soon-generic.mdx"
## Replaying
A replay is a copy of a run with the same payload but against the latest version in that environment. This is useful if something went wrong and you want to try again with the latest version of your code.
### Replaying from the UI
<Tabs>
<Tab title="From a run">
Select a task, then in the bottom right click "Replay" ![Select a task, then in the bottom right
click "Replay"](/images/replay-run-action.png)
</Tab>
<Tab title="Runs list">
<Steps>
<Step title="Click the action button on a run">
![On the runs page, press the triple dot button](/images/replay-runs-list.png)
</Step>
<Step title="Click replay">![Click replay](/images/replay-runs-list-popover.png)</Step>
</Steps>
</Tab>
</Tabs>
### Replaying using the SDK
You can replay a run using the SDK:
```ts
const replayedRun = await runs.replay(run.id);
```
When you call `trigger()` or `batchTrigger()` on a task you receive back a run handle which has an `id` property. You can use that `id` to replay the run.
You can also access the run id from inside a run. You could write this to your database and then replay it later.
```ts
export const simpleChildTask = task({
id: "simple-child-task",
run: async (payload, { ctx }) => {
// the run ID (and other useful info) is in ctx
const runId = ctx.run.id;
},
});
```
### Reattempting
Tasks can [automatically reattempt](/errors-retrying) based on the settings you provide.
Sometimes a task will fail all of its attempts. In that case, you can continue reattempting.
<ComingSoon />
+70
View File
@@ -0,0 +1,70 @@
---
title: "Replaying"
description: "A replay is a copy of a run with the same payload but against the latest version in that environment. This is useful if something went wrong and you want to try again with the latest version of your code."
---
### Replaying from the UI
<Tabs>
<Tab title="From a run">
<Steps>
<Step title="Click the Replay button in the top right">
![Select a task, then in the bottom right
click "Replay"](/images/replay-run-action.png)
</Step>
<Step title="Confirm replay settings">
You can edit the payload <Icon icon="circle-1" iconType="solid" size={20} color="F43F47" /> (if available) and choose the environment <Icon icon="circle-2" iconType="solid" size={20} color="F43F47" /> to replay the run in.
![Select a task, then in the bottom right
click "Replay"](/images/replay-run-modal.png)
</Step>
</Steps>
</Tab>
<Tab title="Runs list">
<Steps>
<Step title="Click the action button on a run">
![On the runs page, press the triple dot button](/images/replay-runs-list.png)
</Step>
<Step title="Click replay">![Click replay](/images/replay-runs-list-popover.png)</Step>
</Steps>
</Tab>
</Tabs>
### Replaying using the SDK
You can replay a run using the SDK:
```ts
const replayedRun = await runs.replay(run.id);
```
When you call `trigger()` or `batchTrigger()` on a task you receive back a run handle which has an `id` property. You can use that `id` to replay the run.
You can also access the run id from inside a run. You could write this to your database and then replay it later.
```ts
export const simpleChildTask = task({
id: "simple-child-task",
run: async (payload, { ctx }) => {
// the run ID (and other useful info) is in ctx
const runId = ctx.run.id;
},
});
```
### Bulk replaying
You can replay multiple runs at once by selecting them from the table on the Runs page using the checkbox on the left hand side of the row. Then click the "Replay runs" button from the bulk action bar that appears at the bottom of the screen.
This is especially useful if you have lots of failed runs and want to run them all again. To do this, first filter the runs by the status you want, then select all the runs you want to replay and click the "Replay runs" button from the bulk action bar at the bottom of the page.
<video
src="https://content.trigger.dev/bulk-replaying-runs.mp4"
preload="auto"
controls={true}
loop
muted
autoPlay={true}
width="100%"
height="100%"
/>
+16
View File
@@ -0,0 +1,16 @@
### Correctly passing event handlers to React components
An issue can sometimes arise when you try to pass a function directly to the `onClick` prop. This is because the function may require specific arguments or context that are not available when the event occurs. By wrapping the function call in an arrow function, you ensure that the handler is called with the correct context and any necessary arguments. For example:
This works:
```tsx
<Button onClick={() => myTask()}>Trigger my task</Button>
```
Whereas this does not work:
```tsx
<Button onClick={myTask}>Trigger my task</Button>
```
+7
View File
@@ -0,0 +1,7 @@
### Next.js build failing due to missing API key in GitHub CI
This issue occurs during the Next.js app build process on GitHub CI where the Trigger.dev SDK is expecting the TRIGGER_SECRET_KEY environment variable to be set at build time. Next.js attempts to compile routes and creates static pages, which can cause issues with SDKs that require runtime environment variables. The solution is to mark the relevant pages as dynamic to prevent Next.js from trying to make them static. You can do this by adding the following line to the route file:
```ts
export const dynamic = "force-dynamic";
```
@@ -0,0 +1 @@
The most common cause of hitting the API rate limit is if youre calling trigger() on a task in a loop, instead of doing this use batchTrigger() which will trigger multiple tasks in a single API call. You can have up to 100 tasks in a single batch trigger call.
+47
View File
@@ -0,0 +1,47 @@
Run your Next.js app:
<CodeGroup>
```bash npm
npm run dev
```
```bash pnpm
pnpm run dev
```
```bash yarn
yarn dev
```
</CodeGroup>
Run the dev server from Step 2. of the [Initial Setup](/guides/frameworks/nextjs#initial-setup) section above if it's not already running:
<CodeGroup>
```bash npm
npx trigger.dev@beta dev
```
```bash pnpm
pnpm dlx trigger.dev@beta dev
```
```bash yarn
yarn dlx trigger.dev@beta dev
```
</CodeGroup>
Now visit the URL in your browser to trigger the task. Ensure the port number is the same as the one you're running your Next.js app on. For example, if you're running your Next.js app on port 3000, visit:
```bash
http://localhost:3000/api/hello-world
```
You should see the CLI log the task run with a link to view the logs in the dashboard.
![Trigger.dev CLI showing a successful run](/images/trigger-cli-run-success.png)
Visit the [Trigger.dev dashboard](https://cloud.trigger.dev) to see your run.
+51
View File
@@ -0,0 +1,51 @@
### Worker failed to start when running Dev command
An issue may occur when trying to run the development command for Trigger.dev when using certain packages like `@t3-oss/env-nextjs` or ORMs like Drizzle ORM. The error message typically indicates that there's a problem with importing ES modules in a CommonJS context.
```bash Error message
X Error: Worker failed to start Error [ERR_REQUIRE_ESM]: require() of ES Module [...] not supported.
Instead change the require of index.js in [...] to a dynamic import() which is available in all CommonJS modules.
```
This issue is related to how Trigger.dev bundles code and interacts with certain ES module dependencies.
To resolve this issue, follow these steps:
1. In your `trigger.config.ts` file, add the problematic dependencies to the `dependenciesToBundle` array:
```bash trigger.config.ts
export const config: TriggerConfig = {
// ... other config options
dependenciesToBundle: [
/@t3-oss/,
"drizzle-orm",
/@neondatabase/,
// Add other problematic dependencies here
],
};
```
2. If you're using environment variables with `@t3-oss/env-nextjs`, implement a `resolveEnvVars` function in your config file:
```bash trigger.config.ts
import { env } from "@/env";
import type { ResolveEnvironmentVariablesFunction } from "@trigger.dev/sdk/v3";
export const resolveEnvVars: ResolveEnvironmentVariablesFunction = () => {
return {
variables: Object.keys(env).map((key) => ({
name: key,
value: env[key as keyof typeof env]?.toString(),
})),
};
};
```
3. For users of packages that require WebSocket (like `@neondatabase/serverless`), you may need to set up a WebSocket polyfill if you're using Node.js versions earlier than 22. Add this to your code:
```bash
import { neonConfig, Pool } from '@neondatabase/serverless';
import ws from 'ws';
neonConfig.webSocketConstructor = ws;
```
+1 -1
View File
@@ -36,7 +36,7 @@ Here's how to trigger a single run from elsewhere in your code:
import { helloWorldTask } from "./trigger/hello-world";
async function triggerHelloWorld() {
//This triggers the task and return a handle
//This triggers the task and returns a handle
const handle = await helloWorld.trigger({ message: "Hello world!" });
//You can use the handle to check the status of the task, cancel and retry it.
+12 -3
View File
@@ -3,7 +3,10 @@ title: "Common problems"
description: "Some common problems you might experience and their solutions"
---
import BundlePackages from '/snippets/bundle-packages.mdx';
import NextjsTroubleshootingMissingApiKey from '/snippets/nextjs-missing-api-key.mdx';
import NextjsTroubleshootingButtonSyntax from '/snippets/nextjs-button-syntax.mdx';
import RateLimitHitUseBatchTrigger from '/snippets/rate-limit-hit-use-batchtrigger.mdx';
import WorkerFailedToStartWhenRunningDevCommand from '/snippets/worker-failed-to-start.mdx';
## Development
@@ -97,9 +100,11 @@ Prisma uses code generation to create the client from your schema file. This mea
Make sure that you always use `await` when you call `trigger`, `triggerAndWait`, `batchTrigger`, and `batchTriggerAndWait`. If you don't then it's likely the task(s) won't be triggered because the calling function process can be terminated before the networks calls are sent.
### `Error: Prisma generate failed to find the specified schema at ../../path`
### Rate limit exceeded
<RateLimitHitUseBatchTrigger/>
View the [rate limits](/limits) page for more information.
## Framework specific issues
@@ -145,4 +150,8 @@ Or change the tsconfig jsx setting:
"jsx": "react-jsx"
},
}
```
```
<NextjsTroubleshootingMissingApiKey/>
<NextjsTroubleshootingButtonSyntax/>
<WorkerFailedToStartWhenRunningDevCommand/>
+3 -3
View File
@@ -8,8 +8,8 @@ Before digging deeper into the details of writing tasks, you should read the [fu
## Writing tasks
| Topic | Description |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| Topic | Description |
| ----------------------------------------------------| ------------------------------------------------------------------------------------------------------------------------- |
| [Logging](/logging) | View and send logs and traces from your tasks. |
| [Errors & retrying](/errors-retrying) | How to deal with errors and write reliable tasks. |
| [Wait](/wait) | Wait for periods of time or for external events to occur before continuing. |
@@ -17,5 +17,5 @@ Before digging deeper into the details of writing tasks, you should read the [fu
| [Versioning](/versioning) | How versioning works. |
| [Machines](/machines) | Configure the CPU and RAM of the machine your task runs on |
| [Idempotency](/idempotency) | Protect against mutations happening twice. |
| [Reattempting & Replaying](/reattempting-replaying) | You can reattempt a task that has failed all of its attempts. You can also replay a task with a new version of your code. |
| [Replaying](/replaying) | You can replay a single task or many at once with a new version of your code. |
| [Notifications](/notifications) | Send realtime notifications from your task that you can subscribe to from your backend or frontend. |