Loading...
Search the Quiltt documentation
Loading...
Build a Next.js app that authenticates users, launches the Connector, and fetches financial data. Learn session token management and avoid rate limit errors.
Time: ~15 minutes Framework: Next.js (concepts apply to any framework)
A Next.js app that:
Session tokens authenticate users for the Connector and GraphQL API:
We'll use QuilttAuthProvider from @quiltt/react to handle token management automatically.
pnpm, yarn, or npm)This tutorial uses pnpm but commands work with any package manager.
Create a Next.js project and install dependencies:
pnpm create next-app quiltt-auth-tutorial --typescript --tailwind --app
cd quiltt-auth-tutorial
pnpm add @quiltt/react
Add your credentials from the Dashboard:
# .env.local
QUILTT_API_KEY_SECRET=your_api_key_secret_here
NEXT_PUBLIC_QUILTT_CONNECTOR_ID=your_connector_id_here
Never commit QUILTT_API_KEY_SECRET to Git. It's for server-side use only.
Start the dev server:
pnpm dev
Create a server-side endpoint to issue tokens (this protects your API key from client-side exposure):
// app/api/quiltt-session/route.ts
import { NextResponse } from 'next/server'
interface CurrentUser {
quilttProfileId: string
}
export async function POST() {
const user = await getCurrentUserFromYourAuthProvider()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!process.env.QUILTT_API_KEY_SECRET) {
return NextResponse.json({ error: 'Missing QUILTT_API_KEY_SECRET' }, { status: 500 })
}
const data = await fetch('https://auth.quiltt.io/v1/users/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.QUILTT_API_KEY_SECRET}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ userId: user.quilttProfileId })
})
const session = await data.json()
return NextResponse.json(session, { status: data.status })
}
async function getCurrentUserFromYourAuthProvider(): Promise<CurrentUser | null> {
// Replace this with your app's server-side auth lookup.
return null
}
The Profile ID must come from the authenticated user, not from request body data sent by the browser.
Fetch tokens and pass to QuilttAuthProvider. This handles session management, Connector auth, and GraphQL client setup.
// app/providers.tsx
'use client'
import { useEffect, useState } from 'react'
import type { PropsWithChildren } from 'react'
import { QuilttAuthProvider } from '@quiltt/react'
interface SessionResponse {
token: string
expiresAt: string
}
export function Providers({
quilttProfileId,
children
}: PropsWithChildren<{ quilttProfileId: string }>) {
const [token, setToken] = useState<string>()
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string>()
useEffect(() => {
async function loadSession() {
// Check for cached token first
const cached = localStorage.getItem(`quiltt_session_${quilttProfileId}`)
if (cached) {
const { token, expiresAt } = JSON.parse(cached) as SessionResponse
// Use cached token if still valid
if (new Date(expiresAt) > new Date()) {
setToken(token)
setLoading(false)
return
}
}
// Fetch new token if no valid cached token
const response = await fetch('/api/quiltt-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
if (!response.ok) throw new Error('Unable to create Quiltt session')
const session = (await response.json()) as SessionResponse
if (!session.token || !session.expiresAt) throw new Error('Invalid Quiltt session')
// Cache the token with expiration
localStorage.setItem(`quiltt_session_${quilttProfileId}`, JSON.stringify({
token: session.token,
expiresAt: session.expiresAt
}))
setToken(session.token)
}
loadSession()
.catch((err: Error) => setError(err.message))
.finally(() => setLoading(false))
}, [quilttProfileId])
if (error) return <div>{error}</div>
if (loading) return <div>Loading...</div>
return (
<QuilttAuthProvider token={token}>
{children}
</QuilttAuthProvider>
)
}
Critical: This code checks localStorage before issuing new tokens. Without caching, every page refresh issues a new token, quickly hitting the 10/hour rate limit.
Only need the Connector (not GraphQL)? Use QuilttProvider instead of QuilttAuthProvider.
Wire up the Providers component:
// app/layout.tsx
import type { PropsWithChildren } from 'react'
import { Providers } from './providers'
export default async function RootLayout({ children }: PropsWithChildren) {
const quilttProfileId = await getQuilttProfileIdForCurrentUser()
return (
<html lang="en">
<body>
<Providers quilttProfileId={quilttProfileId}>
{children}
</Providers>
</body>
</html>
)
}
async function getQuilttProfileIdForCurrentUser(): Promise<string> {
// Replace this with your app's server-side auth lookup.
throw new Error('Missing Profile ID lookup')
}
Add a button that launches the Connector (automatically authenticated):
// app/page.tsx
'use client'
import { QuilttButton } from '@quiltt/react'
export default function Home() {
const connectorId = process.env.NEXT_PUBLIC_QUILTT_CONNECTOR_ID
if (!connectorId) return <div>Missing NEXT_PUBLIC_QUILTT_CONNECTOR_ID</div>
return (
<div className="grid items-center justify-items-center min-h-screen">
<main className="flex flex-col items-center">
<QuilttButton connectorId={connectorId} className="border p-2">
Launch Connector!
</QuilttButton>
</main>
</div>
)
}
Visit http://localhost:3000 and click "Launch Connector":
Use useQuilttClient for GraphQL queries:
// app/components/Welcome.tsx
'use client'
import { useQuilttClient, useQuery, gql } from '@quiltt/react'
const PROFILE_QUERY = gql`
query GetProfile {
profile {
id
email
}
}
`
export default function Welcome() {
const client = useQuilttClient()
const { data, loading } = useQuery(PROFILE_QUERY, { client })
if (loading) return <div>Loading...</div>
return (
<h1 className="text-4xl font-bold text-center py-4">
Welcome, {data?.profile?.email || data?.profile?.id}
</h1>
)
}
Add the Welcome component to your page:
// app/page.tsx
'use client'
import { QuilttButton } from '@quiltt/react'
import Welcome from './components/Welcome'
const CONNECTOR_ID = process.env.NEXT_PUBLIC_QUILTT_CONNECTOR_ID
export default function Home() {
return (
<div className="grid items-center justify-items-center min-h-screen">
<main className="flex flex-col items-center sm:items-start">
<Welcome />
<QuilttButton connectorId={CONNECTOR_ID} className="border p-2">
Launch Connector!
</QuilttButton>
</main>
</div>
)
}
Refresh the page to see the greeting:
Continue Learning:
Reference Documentation: