Connecting Next.js with Supabase Using Prisma

Jun 17, 2025
4 min read

Next.js paired with Supabase and Prisma gives you a powerful, type-safe full‑stack setup. Supabase provides a hosted PostgreSQL database with authentication and real‑time features, while Prisma acts as a modern ORM for type‑safe database access. In this guide, you’ll connect them seamlessly.

Prerequisites

  • Node.js >= 18
  • A Next.js project (TypeScript recommended)
  • A Supabase account and project

1. Create and Configure Your Supabase Project

  1. Log in to app.supabase.com and create a new project.
  2. Note your Project URL and anon/public and service_role keys from Settings > API.

2. Install Dependencies

In your Next.js root folder, run:

npm install @supabase/supabase-js prisma @prisma/client

Initialize Prisma:

npx prisma init

This creates a prisma/schema.prisma file and a .env file.

3. Configure Environment Variables

In .env (next to schema.prisma), add your Supabase credentials:

DATABASE_URL="postgresql://postgres:<YOUR_DB_PASSWORD>@db.<PROJECT_REF>.supabase.co:5432/postgres"
SUPABASE_URL="https://<PROJECT_REF>.supabase.co"
SUPABASE_ANON_KEY="<YOUR_ANON_KEY>"
SUPABASE_SERVICE_ROLE_KEY="<YOUR_SERVICE_ROLE_KEY>"

In your Next.js .env.local:

NEXT_PUBLIC_SUPABASE_URL=$SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY

4. Define Your Prisma Schema

Edit prisma/schema.prisma:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid())
  email     String   @unique
  createdAt DateTime @default(now())
  posts     Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  published Boolean  @default(false)
  createdAt DateTime @default(now())
}

Run migrations:

npx prisma migrate dev --name init

5. Initialize Supabase and Prisma Clients

Create lib/db.ts:

// lib/db.ts
import { createClient } from "@supabase/supabase-js";
import { PrismaClient } from "@prisma/client";

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

declare global {
  // Prevent multiple instances in development
  var prisma: PrismaClient | undefined;
}
export const prisma =
  global.prisma ||
  new PrismaClient({
    log: ["query"],
  });
if (process.env.NODE_ENV !== "production") global.prisma = prisma;

6. Fetch Data in Next.js API Route

Create pages/api/posts.ts:

import type { NextApiRequest, NextApiResponse } from "next";
import { supabase, prisma } from "../../lib/db";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === "GET") {
    // Fetch posts from Supabase
    const { data: sbPosts, error: sbError } = await supabase
      .from("Post")
      .select("*");

    if (sbError) return res.status(500).json({ error: sbError.message });

    // Alternatively, fetch via Prisma
    const prismaPosts = await prisma.post.findMany({
      include: { author: true },
    });

    return res.status(200).json({ supabase: sbPosts, prisma: prismaPosts });
  }
  res.setHeader("Allow", ["GET"]);
  res.status(405).end(`Method ${req.method} Not Allowed`);
}

7. Display in a Next.js Page

In pages/index.tsx:

import { GetServerSideProps } from "next";

type Post = { id: number; title: string; content: string | null };

export const getServerSideProps: GetServerSideProps = async () => {
  const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts`);
  const { prisma } = await res.json();
  return { props: { posts: prisma as Post[] } };
};

export default function Home({ posts }: { posts: Post[] }) {
  return (
    <main>
      <h1>Posts</h1>
      <ul>
        {posts.map((p) => (
          <li key={p.id}>
            <h2>{p.title}</h2>
            <p>{p.content}</p>
          </li>
        ))}
      </ul>
    </main>
  );
}

Conclusion

You’ve now set up a Next.js app that uses Supabase for hosting and authentication and Prisma for type-safe database access. From here, explore real‑time subscriptions, row‑level security in Supabase, and more advanced Prisma queries!

🚀

Combine Supabase and Prisma to deliver a robust, scalable full‑stack experience with minimal boilerplate!