diff --git a/docs/docs.json b/docs/docs.json index 14d728e2db1..19df795e1d8 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -368,6 +368,7 @@ "guides/ai-agents/claude-code-trigger", "guides/frameworks/drizzle", "guides/frameworks/prisma", + "guides/frameworks/nango", "guides/frameworks/sequin", { "group": "Supabase", diff --git a/docs/guides/frameworks/nango.mdx b/docs/guides/frameworks/nango.mdx new file mode 100644 index 00000000000..91c3ad8f738 --- /dev/null +++ b/docs/guides/frameworks/nango.mdx @@ -0,0 +1,287 @@ +--- +title: "Nango OAuth with Trigger.dev" +sidebarTitle: "Nango OAuth guide" +description: "Use Nango to authenticate API calls inside a Trigger.dev task, no token management required." +icon: "key" +--- + +[Nango](https://www.nango.dev/) handles OAuth for 250+ APIs, storing and automatically refreshing access tokens on your behalf. This makes it a natural fit for Trigger.dev tasks that need to call third-party APIs on behalf of your users. + +In this guide you'll build a task that: + +1. Receives a Nango `connectionId` from your frontend +2. Fetches a fresh GitHub access token from Nango inside the task +3. Calls the GitHub API to retrieve the user's open pull requests +4. Uses Claude to summarize what's being worked on + +This pattern works for any API Nango supports. Swap GitHub for HubSpot, Slack, Notion, or any other provider. + +## Prerequisites + +- A Next.js project with [Trigger.dev installed](/guides/frameworks/nextjs) +- A [Nango](https://app.nango.dev/) account +- An [Anthropic](https://console.anthropic.com/) API key + +## How it works + +```mermaid +sequenceDiagram + participant User + participant Frontend + participant API as Next.js API + participant TD as Trigger.dev task + participant Nango + participant GH as GitHub API + participant Claude + + User->>Frontend: Clicks "Analyze my PRs" + Frontend->>API: POST /api/nango-session + API->>Nango: POST /connect/sessions (secret key) + Nango-->>API: session token (30 min TTL) + API-->>Frontend: session token + Frontend->>Nango: OAuth connect (frontend SDK + session token) + Nango-->>Frontend: connectionId + Frontend->>API: POST /api/analyze-prs { connectionId, repo } + API->>TD: tasks.trigger(...) + TD->>Nango: getConnection(connectionId) + Nango-->>TD: access_token + TD->>GH: GET /repos/:repo/pulls + GH-->>TD: open pull requests + TD->>Claude: Summarize PRs + Claude-->>TD: Summary +``` + +## Step 1: Connect GitHub in Nango + + + + 1. In your [Nango dashboard](https://app.nango.dev/), go to **Integrations** and click **Set up new integration**. + 2. Search for **GitHub** and select GitHub (User OAuth). + 3. Create and add a test connection + + + Install the Nango frontend SDK in your Next.js project: + + ```bash + npm install @nangohq/frontend + ``` + + The frontend SDK requires a short-lived **connect session token** issued by your backend. Add an API route that creates the session: + + ```ts app/api/nango-session/route.ts + import { NextResponse } from "next/server"; + + export async function POST(req: Request) { + const { userId } = await req.json(); + + if (!userId || typeof userId !== "string") { + return NextResponse.json({ error: "Missing or invalid userId" }, { status: 400 }); + } + + const response = await fetch("https://api.nango.dev/connect/sessions", { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.NANGO_SECRET_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + end_user: { id: userId }, + }), + }); + + if (!response.ok) { + const text = await response.text(); + console.error("Nango error:", response.status, text); + return NextResponse.json({ error: text }, { status: response.status }); + } + + const { data } = await response.json(); + return NextResponse.json({ token: data.token }); + } + ``` + + Then add a connect button to your UI that fetches the token and opens the Nango OAuth flow: + + ```tsx app/page.tsx + "use client"; + + import Nango from "@nangohq/frontend"; + + export default function Page() { + async function connectGitHub() { + // Get a short-lived session token from your backend + const sessionRes = await fetch("/api/nango-session", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ userId: "user_123" }), // replace with your actual user ID + }); + const { token } = await sessionRes.json(); + + const nango = new Nango({ connectSessionToken: token }); + // Use the exact integration slug from your Nango dashboard + const result = await nango.auth(""); + + // result.connectionId is what you pass to your task + await fetch("/api/analyze-prs", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + connectionId: result.connectionId, + repo: "triggerdotdev/trigger.dev", + }), + }); + } + + return ; + } + ``` + + + + +## Step 2: Create the Trigger.dev task + +Install the required packages: + +```bash +npm install @nangohq/node @anthropic-ai/sdk +``` + +Create the task: + + + +```ts trigger/analyze-prs.ts +import { task } from "@trigger.dev/sdk"; +import { Nango } from "@nangohq/node"; +import Anthropic from "@anthropic-ai/sdk"; + +const nango = new Nango({ secretKey: process.env.NANGO_SECRET_KEY! }); +const anthropic = new Anthropic(); + +export const analyzePRs = task({ + id: "analyze-prs", + run: async (payload: { connectionId: string; repo: string }) => { + const { connectionId, repo } = payload; + + // Fetch a fresh access token from Nango. It handles refresh automatically. + // Use the exact integration slug from your Nango dashboard, e.g. "github-getting-started" + const connection = await nango.getConnection("", connectionId); + + if (connection.credentials.type !== "OAUTH2") { + throw new Error(`Unexpected credentials type: ${connection.credentials.type}`); + } + + const accessToken = connection.credentials.access_token; + // Call the GitHub API on behalf of the user + const response = await fetch( + `https://api.github.com/repos/${repo}/pulls?state=open&per_page=20`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/vnd.github.v3+json", + }, + } + ); + + if (!response.ok) { + throw new Error(`GitHub API error: ${response.status} ${response.statusText}`); + } + + const prs = await response.json(); + + if (prs.length === 0) { + return { summary: "No open pull requests found.", prCount: 0 }; + } + + // Use Claude to summarize what's being worked on + const prList = prs + .map( + (pr: { number: number; title: string; user: { login: string }; body: string | null }) => + `#${pr.number} by @${pr.user.login}: ${pr.title}\n${pr.body?.slice(0, 200) ?? ""}` + ) + .join("\n\n"); + + const message = await anthropic.messages.create({ + model: "claude-opus-4-6", + max_tokens: 1024, + messages: [ + { + role: "user", + content: `Here are the open pull requests for ${repo}. Give a concise summary of what's being worked on, grouped by theme where possible.\n\n${prList}`, + }, + ], + }); + + const summary = message.content[0].type === "text" ? message.content[0].text : ""; + + return { summary, prCount: prs.length }; + }, +}); +``` + + + +## Step 3: Create the API route + +Add a route handler that receives the `connectionId` from your frontend and triggers the task: + +```ts app/api/analyze-prs/route.ts +import { analyzePRs } from "@/trigger/analyze-prs"; +import { NextResponse } from "next/server"; + +export async function POST(req: Request) { + const { connectionId, repo } = await req.json(); + + if (!connectionId || !repo) { + return NextResponse.json({ error: "Missing connectionId or repo" }, { status: 400 }); + } + + const handle = await analyzePRs.trigger({ connectionId, repo }); + + return NextResponse.json(handle); +} +``` + +## Step 4: Set environment variables + +Add the following to your `.env.local` file: + +```bash +NANGO_SECRET_KEY= # From Nango dashboard → Environment → Secret key +TRIGGER_SECRET_KEY= # From Trigger.dev dashboard → API keys +ANTHROPIC_API_KEY= # From Anthropic console +``` + +Add `NANGO_SECRET_KEY` and `ANTHROPIC_API_KEY` as [environment variables](/deploy-environment-variables) in your Trigger.dev project too. These are used inside the task at runtime. + +## Test it + + + + + ```bash + npm run dev + npx trigger.dev@latest dev + ``` + + + + Open your app, click **Analyze my PRs**, and complete the GitHub OAuth flow. The task will be triggered automatically. + + + Open your [Trigger.dev dashboard](https://cloud.trigger.dev/) and navigate to **Runs** to see the task execute. You'll see the PR count and Claude's summary in the output. + + + + + Your task is now fetching a fresh GitHub token from Nango, calling the GitHub API on behalf of the + user, and using Claude to summarize their open PRs. No token storage or refresh logic required. + + +## Next steps + +- **Reuse the `connectionId`**: Once a user has connected, store their `connectionId` and pass it in future task payloads. No need to re-authenticate. +- **Add retries**: If the GitHub API returns a transient error, Trigger.dev [retries](/errors-retrying) will handle it automatically. +- **Switch providers**: The same pattern works for any Nango-supported API. Change `"github"` to `"hubspot"`, `"slack"`, `"notion"`, or any other provider. +- **Stream the analysis**: Use [Trigger.dev Realtime](/realtime/overview) to stream Claude's response back to your frontend as it's generated.