Hygraph
Docs

#Use app tokens

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:

#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.

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.

  1. 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 },
    );
    }
    }
  2. 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.