#Use app tokens
If your app makes API calls using a token injected directly by the App SDK, you need to update it to use the App Token exchange flow described on this page. If your app already uses this flow or does not make API calls, no action is needed.
Once you've updated and redeployed your app, reinstall it in your project and notify our team so we can apply the patch on our end.
App tokens let your app call Hygraph APIs on behalf of the environment it's installed in. You only need to generate an app token if your app accesses the Content API, Management API, or webhooks.
The token is generated during installation by exchanging a setup code for an app token. The setup code is passed as a code query parameter and is only available while installation is in progress.
#Prerequisites
Before you start, make sure you have:
- Registered your app and saved the
Client IDandClient Secretfrom the Permissions tab. - Created your app using
create-next-appwith the App Router.
#Step 1: Add the token exchange API route
Create app/api/exchangeSetupCode/route.ts. This route receives the setup code from your setup page, calls the Hygraph token exchange endpoint with your app credentials, and returns the app token.
Store CLIENT_ID and CLIENT_SECRET as environment variables. Never commit them to your repository.
import { NextResponse } from "next/server";type ExchangeSetupCodePayload = {code?: string;environmentId?: string;managementApiBaseUrl?: string;};export async function POST(request: Request) {let payload: ExchangeSetupCodePayload;try {payload = (await request.json()) as ExchangeSetupCodePayload;} catch {return NextResponse.json({ message: "Invalid request body. JSON payload is required." },{ status: 400 },);}const { code, environmentId, managementApiBaseUrl } = payload;if (!code || !environmentId || !managementApiBaseUrl) {return NextResponse.json({message:"Missing required fields: code, environmentId, and managementApiBaseUrl are required.",},{ status: 400 },);}if (!process.env.CLIENT_ID || !process.env.CLIENT_SECRET) {return NextResponse.json({ message: "Server is missing CLIENT_ID or CLIENT_SECRET." },{ status: 500 },);}try {const response = await fetch(`${managementApiBaseUrl}/app-exchange-token`, {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({clientId: process.env.CLIENT_ID,clientSecret: process.env.CLIENT_SECRET,exchangeCode: code,}),});const data = (await response.json()) as {appToken?: string;message?: string;};if (!response.ok) {return NextResponse.json({message:data.message ?? "Failed to exchange setup code for an app token.",},{ status: response.status },);}if (!data.appToken) {return NextResponse.json({ message: "Hygraph did not return an app token." },{ status: 502 },);}return NextResponse.json({ appToken: data.appToken });} catch {return NextResponse.json({ message: "Could not reach Hygraph token exchange endpoint." },{ status: 502 },);}}
#Step 2: Add the setup page
Create app/setup/page.tsx. This page reads the setup code from the URL query parameter, calls your exchange route, and marks the installation as completed.
"use client";import { useApp, Wrapper } from "@hygraph/app-sdk-react";import { useSearchParams } from "next/navigation";import { useState } from "react";function Setup({ code }: { code: string }) {const { context, updateInstallation } = useApp();const [isSubmitting, setIsSubmitting] = useState(false);const [error, setError] = useState<string | null>(null);const managementApiBaseUrl = context.project.mgmtApi.split("/").slice(0, -1).join("/");const handleConnectApp = async () => {setError(null);setIsSubmitting(true);try {if (!code) {throw new Error("Missing setup code. Please restart the app installation.",);}const response = await fetch(`/api/exchangeSetupCode`, {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({code,environmentId: context.environment.id,managementApiBaseUrl,}),});if (!response.ok) {let message = "Failed to connect your app. Please try again.";try {const errorData = await response.json();if (errorData?.message) {message = errorData.message;}} catch {// Use default message when response body is not JSON}throw new Error(message);}await updateInstallation({ status: "COMPLETED", config: {} });} catch (caughtError) {setError(caughtError instanceof Error? caughtError.message: "Something went wrong while connecting your app.",);} finally {setIsSubmitting(false);}};return (<><button onClick={handleConnectApp} disabled={isSubmitting}>{isSubmitting ? "Connecting..." : "Connect your app"}</button>{error ? <p role="alert">{error}</p> : null}</>);}function SetupContent({ code }: { code: string }) {const { installation } = useApp();if (!installation) {return <div>Loading installation...</div>;}if (installation.status === "COMPLETED") {return <div>Installation completed</div>;}return <Setup code={code} />;}export default function SetupPage() {const searchParams = useSearchParams();const code = searchParams.get("code") ?? "";return (<Wrapper><SetupContent code={code} /></Wrapper>);}
#Step 3: Make authenticated API calls
Once installation is complete, retrieve the app token from context.environment.authToken and pass it as a Bearer token in your API requests.
The example below shows an API route that creates a model using the Management API and a component that calls it.
-
In
app/api/createHygraphModel/route.ts, create an API route that creates a model using the Management API:import { NextResponse } from "next/server";const createModelMutation = `mutation CreateModelMutation($input: CreateModelInput!) {createModel(data: $input) {migration {id}}}`;type CreateHygraphModelPayload = {appToken?: string;environmentId?: string;managementApiUrl?: string;};export async function POST(request: Request) {let body: CreateHygraphModelPayload;try {body = (await request.json()) as CreateHygraphModelPayload;} catch {return NextResponse.json({ message: "Invalid request body. JSON payload is required." },{ status: 400 },);}if (!body.appToken || !body.environmentId || !body.managementApiUrl) {return NextResponse.json({message:"Missing required fields: appToken, environmentId, and managementApiUrl are required.",},{ status: 400 },);}try {const response = await fetch(body.managementApiUrl, {method: "POST",headers: {"Content-Type": "application/json",Authorization: `Bearer ${body.appToken}`,},body: JSON.stringify({query: createModelMutation,variables: {input: {environmentId: body.environmentId,apiId: "DemoModel",apiIdPlural: "DemoModels",displayName: "Demo Model",description: "Demo Model Description",},},}),});const data = (await response.json()) as Record<string, unknown>;if (!response.ok) {return NextResponse.json({ message: "Failed to create Hygraph model.", data },{ status: response.status },);}return NextResponse.json(data, { status: 200 });} catch {return NextResponse.json({ message: "Could not reach Hygraph management API endpoint." },{ status: 502 },);}} -
In
app/page.tsx, create a component that calls the route:"use client";import { Wrapper, useApp } from "@hygraph/app-sdk-react";import { useState } from "react";function CreateHygraphModel() {const { context } = useApp();const [isSubmitting, setIsSubmitting] = useState(false);const [message, setMessage] = useState<string | null>(null);const appToken = context.environment.authToken;const handleCreateModel = async () => {setMessage(null);setIsSubmitting(true);const response = await fetch("/api/createHygraphModel", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({appToken,environmentId: context.environment.id,managementApiUrl: context.project.mgmtApi,}),});const data = (await response.json()) as { message?: string };setMessage("🎉 Created Hygraph Model");setIsSubmitting(false);};return (<><button onClick={handleCreateModel} disabled={isSubmitting}>{isSubmitting ? "Creating..." : "Create Hygraph Model"}</button>{message ? <p>{message}</p> : null}</>);}export default function PageRoute() {return (<Wrapper><CreateHygraphModel /></Wrapper>);}
#What's next
- Register an app: How to configure app permissions and retrieve your Client ID and Client Secret.
- Install an app: How the installation flow works from the user's perspective.
- First steps: Step-by-step tutorial for building an app with field and sidebar elements.
- API Reference: Full reference for common props, including
context.environment.authToken.