Next.js & Prisma
Learn how to integrate @onlyfansapi/auth into a Next.js application with Prisma for persistent account storage.
Overview
This guide walks you through building a complete account management dashboard that:
- Authenticates OnlyFans accounts using
@onlyfansapi/auth - Stores account data in a PostgreSQL database via Prisma
- Provides a UI to view and manage connected accounts
Example Repository
The complete source code is available at auth-example-nextjs-prisma.
Prerequisites
Before starting, ensure you have:
- Node.js 18+ or Bun installed
- A PostgreSQL database
- An OnlyFans API key from app.onlyfansapi.com/api-keys
Step 1: Create a Next.js Project
Create a new Next.js project with TypeScript and Tailwind CSS:
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-appStep 2: Install Dependencies
Install the required packages:
npm install @onlyfansapi/auth prisma @prisma/client @prisma/adapter-pg pgStep 3: Set Up Prisma
Initialize Prisma with PostgreSQL:
npx prisma initUpdate your prisma/schema.prisma to define the Account model:
generator client {
provider = "prisma-client"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Account {
id String @id @default(cuid())
accountId String @unique // OnlyFans account ID
username String // OnlyFans username
displayName String // User-defined label
name String? // OnlyFans profile name
avatar String? // Avatar URL
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Create the Prisma client singleton in lib/prisma.ts:
import { Pool } from "pg";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
function createPrismaClient() {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaPg(pool);
return new PrismaClient({ adapter });
}
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}Step 4: Configure Environment Variables
Create a .env file with your credentials:
# PostgreSQL connection string
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
# OnlyFans API Key (keep this secret, server-side only)
OFAPI_API_KEY="ofapi_..."Security Note
The OFAPI_API_KEY should never be exposed to the client. We'll create a server-side endpoint to generate client session tokens.
Step 5: Create the Client Session API Route
Create app/api/client-session/route.ts to securely generate client session tokens:
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const body = await request.json();
const { displayName } = body;
if (!displayName) {
return NextResponse.json(
{ error: "displayName is required" },
{ status: 400 }
);
}
const apiKey = process.env.OFAPI_API_KEY;
if (!apiKey) {
return NextResponse.json(
{ error: "API key not configured" },
{ status: 500 }
);
}
// Create client session via OnlyFans API
const response = await fetch(
"https://app.onlyfansapi.com/api/client-sessions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ display_name: displayName }),
}
);
if (!response.ok) {
return NextResponse.json(
{ error: "Failed to create client session" },
{ status: response.status }
);
}
const data = await response.json();
return NextResponse.json({ token: data.data.token });
}This endpoint calls the Create Client Session API to generate a temporary token for authentication.
Step 6: Create the Accounts API Routes
Create app/api/accounts/route.ts for listing and creating accounts:
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
export async function GET() {
const accounts = await prisma.account.findMany({
orderBy: { createdAt: "desc" },
});
return NextResponse.json(accounts);
}
export async function POST(request: NextRequest) {
const { accountId, username, displayName, name, avatar } = await request.json();
if (!accountId || !username || !displayName) {
return NextResponse.json(
{ error: "accountId, username, and displayName are required" },
{ status: 400 }
);
}
const account = await prisma.account.create({
data: { accountId, username, displayName, name, avatar },
});
return NextResponse.json(account, { status: 201 });
}Create app/api/accounts/[id]/route.ts for deleting accounts:
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
await prisma.account.delete({ where: { id } });
return NextResponse.json({ success: true });
}Step 7: Build the Dashboard UI
Create a dashboard page in app/page.tsx that uses the auth package:
"use client";
import { useState } from "react";
import { startOnlyFansAuthentication } from "@onlyfansapi/auth";
export default function Dashboard() {
const [displayName, setDisplayName] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleAddAccount = async () => {
if (!displayName.trim()) return;
setIsLoading(true);
try {
// Step 1: Get client session token from our backend
const sessionRes = await fetch("/api/client-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ displayName: displayName.trim() }),
});
if (!sessionRes.ok) throw new Error("Failed to create session");
const { token } = await sessionRes.json();
// Step 2: Start authentication with the token
startOnlyFansAuthentication(token, {
onSuccess: async (data) => {
// Step 3: Save account to database
await fetch("/api/accounts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
accountId: data.accountId,
username: data.username,
displayName: displayName.trim(),
name: data.onlyfansData?.name,
avatar: data.onlyfansData?.avatar_url,
}),
});
// Refresh account list or show success message
window.location.reload();
},
onError: (error) => {
console.error("Authentication failed:", error.message);
setIsLoading(false);
},
});
} catch (error) {
console.error("Error:", error);
setIsLoading(false);
}
};
return (
<div>
<h1>Account Dashboard</h1>
<input
type="text"
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
placeholder="Enter display name"
/>
<button onClick={handleAddAccount} disabled={isLoading}>
{isLoading ? "Connecting..." : "Add Account"}
</button>
</div>
);
}Step 8: Initialize the Database
Generate the Prisma client and push the schema to your database:
npx prisma generate
npx prisma db pushStep 9: Run the Application
Start the development server:
npm run devOpen http://localhost:3000 to see your dashboard.
Authentication Flow Summary
- User initiates auth: Clicks "Add Account" and enters a display name
- Backend creates session: Server calls OnlyFans API to create a client session token
- Auth popup opens:
startOnlyFansAuthentication()opens a secure popup - User authenticates: User logs into their OnlyFans account
- Callback fires:
onSuccessreceives account data - Account saved: Backend stores account in database via Prisma
Best Practices
Keep API Keys Server-Side
Never expose your OFAPI_API_KEY to the client. Always create client session tokens via a server-side API route.
// โ
Good: Server-side token generation
const response = await fetch("/api/client-session", { ... });
// โ Bad: Client-side API key usage
startOnlyFansAuthentication("ofapi_...", { ... });Handle Duplicate Accounts
Check for existing accounts before creating:
const existing = await prisma.account.findUnique({
where: { accountId: data.accountId },
});
if (existing) {
// Update existing account or show error
}Store Display Names
The displayName field helps users identify accounts in your dashboard:
body: JSON.stringify({
accountId: data.accountId,
username: data.username,
displayName: "Agency / Model: Stella", // User-defined label
})TypeScript Support
The auth package includes full TypeScript definitions:
import {
startOnlyFansAuthentication,
AuthSuccessData,
AuthFailureError
} from "@onlyfansapi/auth";
startOnlyFansAuthentication(token, {
onSuccess: (data: AuthSuccessData) => {
console.log(data.accountId); // string
console.log(data.username); // string
console.log(data.onlyfansData.name); // string
},
onError: (error: AuthFailureError) => {
console.log(error.message); // string
},
});Next Steps
- Add webhooks: Listen for account events via Webhooks
- Explore the API: Use account credentials for API calls
- View example repo: See the complete implementation at auth-example-nextjs-prisma