# Next.js & Prisma (/auth/examples/nextjs-prisma)



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

<Callout title="Example Repository">
  The complete source code is available at [auth-example-nextjs-prisma](https://github.com/onlyfansapi/auth-example-nextjs-prisma).
</Callout>

## 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](https://app.onlyfansapi.com/api-keys)

## Step 1: Create a Next.js Project

Create a new Next.js project with TypeScript and Tailwind CSS:

```bash
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
```

## Step 2: Install Dependencies

Install the required packages:

```bash
npm install @onlyfansapi/auth prisma @prisma/client @prisma/adapter-pg pg
```

## Step 3: Set Up Prisma

Initialize Prisma with PostgreSQL:

```bash
npx prisma init
```

Update your `prisma/schema.prisma` to define the Account model:

```prisma
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`:

```typescript
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:

```bash
# PostgreSQL connection string
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

# OnlyFans API Key (keep this secret, server-side only)
OFAPI_API_KEY="ofapi_..."
```

<Callout title="Security Note" type="warn">
  The `OFAPI_API_KEY` should never be exposed to the client. We'll create a server-side endpoint to generate client session tokens.
</Callout>

## Step 5: Create the Client Session API Route

Create `app/api/client-session/route.ts` to securely generate client session tokens:

```typescript
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-reference/client-sessions/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:

```typescript
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:

```typescript
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:

```tsx
"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:

```bash
npx prisma generate
npx prisma db push
```

## Step 9: Run the Application

Start the development server:

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) to see your dashboard.

## Authentication Flow Summary

<Mermaid
  chart="sequenceDiagram
    participant User
    participant Backend as Backend<br/>/api/client-session
    participant OFAPI as OnlyFans API
    participant Auth as @onlyfansapi/auth
    participant Popup as Auth Popup
    participant DB as Database (Prisma)

    User->>Backend: Click &#x22;Add Account&#x22;
    Backend->>OFAPI: POST /api/client-sessions
    OFAPI-->>Backend: token (ofapi_cs_...)
    Backend->>Auth: startOnlyFansAuthentication(token)
    Auth->>Popup: Open authentication popup
    Popup->>Popup: User logs in
    Popup-->>Auth: onSuccess(data)
    Auth->>Backend: POST /api/accounts
    Backend->>DB: Save account
    DB-->>User: Account connected!"
/>

1. **User initiates auth**: Clicks "Add Account" and enters a display name
2. **Backend creates session**: Server calls OnlyFans API to create a client session token
3. **Auth popup opens**: `startOnlyFansAuthentication()` opens a secure popup
4. **User authenticates**: User logs into their OnlyFans account
5. **Callback fires**: `onSuccess` receives account data
6. **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.

```typescript
// ✅ 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:

```typescript
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:

```typescript
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:

```typescript
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](/webhooks)
* **Explore the API**: Use account credentials for [API calls](/api-reference)
* **View example repo**: See the complete implementation at [auth-example-nextjs-prisma](https://github.com/onlyfansapi/auth-example-nextjs-prisma)

## Related Resources

* [Installation Guide](/auth/installation)
* [Quick Start](/auth/quickstart)
* [Create Client Session API](/api-reference/client-sessions/create-client-session)
* [API Reference](/api-reference)
