Quickstart
Add a complete Connect flow to a Next.js app.
This guide adds a complete Connect flow to a Next.js app. Your app opens Vana in a second tab, then displays the approved data in the original tab.
Before you start
You need an Open Data Labs account and a Next.js app. In the dashboard, create an app. Add http://localhost:3000 to its Embed Origins. Copy its API key and public app ID.
Step 1: Create the app
Create a Next.js app, then install the Connect SDK.
npx create-next-app@latest my-odl-app --ts --tailwind --app --no-src-dir --import-alias "@/*" --use-npm --yes
cd my-odl-app
npm install @opendatalabs/connect-jsStep 2: Add environment variables
Create .env.local in the project root.
ODL_API_BASE_URL=https://api.opendatalabs.com/api/v1
ODL_API_KEY=your_api_key
ODL_APP_ID=odl_app_123
APP_URL=http://localhost:3000
NEXT_PUBLIC_VANA_CONNECT_OPENING_URL=https://app.vana.org/connect/openingStep 3: Create the Connect controller
Create lib/odl.ts.
import { createConnectController } from "@opendatalabs/connect-js/server";
export const odl = createConnectController({
apiBaseUrl: process.env.ODL_API_BASE_URL!,
apiKey: process.env.ODL_API_KEY!,
appId: process.env.ODL_APP_ID!,
defaultOrigin: process.env.APP_URL!,
source: "instagram",
scopes: ["read:profile", "read:posts"],
});This example requests Instagram profile and post data. Choose a source and its available scopes from the source catalog.
Step 4: Create the server routes
Create these routes in your app.
// app/api/connect/session/route.ts
import { odl } from "@/lib/odl";
export async function POST() {
return Response.json(
await odl.createConnectSession({
redirectUrl: process.env.APP_URL!,
})
);
}// app/api/connect/status/route.ts
import { odl } from "@/lib/odl";
export async function GET(request: Request) {
const connectionId = new URL(request.url).searchParams.get("connectionId");
if (!connectionId) {
return Response.json({ error: "Missing connectionId" }, { status: 400 });
}
return Response.json(await odl.getStatus(connectionId));
}// app/api/connect/data/route.ts
import { odl } from "@/lib/odl";
export async function GET(request: Request) {
const connectionId = new URL(request.url).searchParams.get("connectionId");
if (!connectionId) {
return Response.json({ error: "Missing connectionId" }, { status: 400 });
}
return Response.json(await odl.readAllWhenReady(connectionId));
}Step 5: Add the Connect page
Replace app/page.tsx with this client component.
"use client";
import { useState } from "react";
import { useTwoTabConnect } from "@opendatalabs/connect-js/react";
async function getJson(url: string, init?: RequestInit) {
const response = await fetch(url, init);
if (!response.ok) {
throw new Error(await response.text());
}
return response.json();
}
export default function Home() {
const [result, setResult] = useState<unknown>(null);
const [error, setError] = useState<string | null>(null);
const connect = useTwoTabConnect({
openingUrl: process.env.NEXT_PUBLIC_VANA_CONNECT_OPENING_URL,
createSession: () => getJson("/api/connect/session", { method: "POST" }),
getStatus: (connectionId) =>
getJson(`/api/connect/status?connectionId=${encodeURIComponent(connectionId)}`),
readResult: (connectionId) =>
getJson(`/api/connect/data?connectionId=${encodeURIComponent(connectionId)}`),
});
async function start() {
setError(null);
try {
setResult(await connect.start());
} catch (cause) {
setError(cause instanceof Error ? cause.message : "Connect failed.");
}
}
return (
<main className="mx-auto flex min-h-screen max-w-xl flex-col justify-center gap-6 p-6">
<div className="space-y-2">
<p className="text-sm font-medium text-sky-700">Open Data Labs</p>
<h1 className="text-3xl font-semibold tracking-tight">
Connect Instagram
</h1>
<p className="text-slate-600">
Connect your data in Vana, then return here to see the result.
</p>
</div>
<button
className="w-fit rounded-md bg-sky-600 px-4 py-2 font-medium text-white shadow-sm transition hover:bg-sky-700 disabled:cursor-not-allowed disabled:opacity-60"
disabled={connect.state.type !== "idle"}
onClick={start}
type="button"
>
{connect.state.type === "idle" ? "Connect Instagram" : "Connecting..."}
</button>
{error ? <p className="text-sm text-red-700">{error}</p> : null}
{result ? (
<pre className="max-h-96 overflow-auto rounded-lg bg-slate-950 p-4 text-sm text-slate-50">
{JSON.stringify(result, null, 2)}
</pre>
) : null}
</main>
);
}Step 6: Run the flow
Start the app.
npm run devOpen http://localhost:3000 and select Connect Instagram. Vana opens in a second tab. Complete the request there. Return to the original tab.
You are done when the original tab displays JSON for the approved scopes.
Questions, feature requests, or support: [email protected]
Updated 17 days ago
