使用 Prisma、Supabase 和 Shadcn 设置 Next.js 项目。

2025-05-25

使用 Prisma、Supabase 和 Shadcn 设置 Next.js 项目。

设置 Next.js

首先运行以下命令,使用 supabase、typescript、tailwind 初始化下一个 js 项目:npx create-next-app@latest。选择全部默认选项:

设置 Prisma

运行以下命令安装 prisma:
npm install prisma --save-dev

安装 prisma 后,运行以下命令来初始化模式文件和 .env 文件:
npx prisma init

现在应该有一个 .env 文件。你应该添加 database_url 来将 prisma 连接到你的数据库。应该如下所示:

// .env
DATABASE_URL=url
Enter fullscreen mode Exit fullscreen mode

在你的 schema.prisma 中你应该添加你的模型,我现在只是使用一些随机模型:

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

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

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

model User {
  id            String       @default(cuid()) @id
  name          String?
  email         String?   @unique
  createdAt     DateTime  @default(now()) @map(name: "created_at")
  updatedAt     DateTime  @updatedAt @map(name: "updated_at")
  posts         Post[]
  @@map(name: "users")
}
Enter fullscreen mode Exit fullscreen mode

现在您可以运行以下命令将您的数据库与您的模式同步:
npx prisma db push

为了在客户端访问 prisma,您需要安装 prisma 客户端。您可以通过运行以下命令来执行此操作:
npm install @prisma/client

您的客户端也必须与您的模式同步,您可以通过运行以下命令来执行此操作:
npx prisma generate

当你运行时,npx prisma db push会自动调用生成命令。

为了访问 prisma 客户端,您需要创建它的一个实例,因此在 src 目录中创建一个名为 lib 的新文件夹,并向其中添加一个名为 prisma.ts 的新文件。

// prisma.ts

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default prisma;
Enter fullscreen mode Exit fullscreen mode

现在您可以在任何文件中导入相同的 Prisma 实例。

设置Shadcn

首先运行以下命令开始设置shadcn:
npx shadcn-ui@latest init

我选择了以下选项:
typescript:是
样式:默认
基色:slate
全局css:src/app/globals.css
css变量:是
tailwind配置:tailwind.config.ts
组件:@/components(默认)
utils:@/lib/utils(默认)
react服务器组件:是
写入components.json:是

接下来运行以下命令来设置下一个主题:
npm install next-themes

然后将一个名为 theme-provider.tsx 的文件添加到您的组件库并添加以下代码:

// theme-provider.tsx

"use client"

import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
Enter fullscreen mode Exit fullscreen mode

设置好提供程序后,您需要将其添加到layout.tsx文件中,以便在整个应用中实现它。使用主题提供程序包装{children},如下所示:

// layout.tsx
  return (
    <html lang="en" suppressHydrationWarning>
      <body className={inter.className}>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
Enter fullscreen mode Exit fullscreen mode

现在转到 shadcn主题页面。选择要使用的主题,然后按复制代码。然后将复制的代码添加到 globals.css 中,如下所示:

// globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 224 71.4% 4.1%;
    --card: 0 0% 100%;
    --card-foreground: 224 71.4% 4.1%;
    --popover: 0 0% 100%;
    --popover-foreground: 224 71.4% 4.1%;
    --primary: 262.1 83.3% 57.8%;
    --primary-foreground: 210 20% 98%;
    --secondary: 220 14.3% 95.9%;
    --secondary-foreground: 220.9 39.3% 11%;
    --muted: 220 14.3% 95.9%;
    --muted-foreground: 220 8.9% 46.1%;
    --accent: 220 14.3% 95.9%;
    --accent-foreground: 220.9 39.3% 11%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 20% 98%;
    --border: 220 13% 91%;
    --input: 220 13% 91%;
    --ring: 262.1 83.3% 57.8%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 224 71.4% 4.1%;
    --foreground: 210 20% 98%;
    --card: 224 71.4% 4.1%;
    --card-foreground: 210 20% 98%;
    --popover: 224 71.4% 4.1%;
    --popover-foreground: 210 20% 98%;
    --primary: 263.4 70% 50.4%;
    --primary-foreground: 210 20% 98%;
    --secondary: 215 27.9% 16.9%;
    --secondary-foreground: 210 20% 98%;
    --muted: 215 27.9% 16.9%;
    --muted-foreground: 217.9 10.6% 64.9%;
    --accent: 215 27.9% 16.9%;
    --accent-foreground: 210 20% 98%;
    --destructive: 0 62.8% 30.6%;
    --destructive-foreground: 210 20% 98%;
    --border: 215 27.9% 16.9%;
    --input: 215 27.9% 16.9%;
    --ring: 263.4 70% 50.4%;
  }
}
Enter fullscreen mode Exit fullscreen mode

现在您应该能够在您的项目中使用 shadcn 组件和主题。

设置 Supabase

第一步是创建一个新的 supabase 项目。接下来,安装 next.js 身份验证帮助库:
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js

现在,你需要将 supabase 的 URL 和匿名密钥添加到你的 .env 文件中。你的 .env 文件现在应该如下所示:

// .env
DATABASE_URL=url
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
Enter fullscreen mode Exit fullscreen mode

我们将使用 supabase cli 根据我们的模式生成类型。使用以下命令安装该 cli:
npm install supabase --save-dev

为了登录 supabase,请运行npx supabase login它,它会自动登录。

现在我们可以通过运行以下命令来生成我们的类型:
npx supabase gen types typescript --project-id YOUR_PROJECT_ID > src/lib/database.types.ts该命令应该在您的 lib 文件夹中添加一个新文件,其中包含基于您的模式的类型。

现在在项目根目录中创建一个 middleware.ts 文件并添加以下代码:

import { createMiddlewareClient } from "@supabase/auth-helpers-nextjs";
import { NextResponse } from "next/server";

import type { NextRequest } from "next/server";
import type { Database } from "@/lib/database.types";

export async function middleware(req: NextRequest) {
  const res = NextResponse.next();
  const supabase = createMiddlewareClient<Database>({ req, res });
  await supabase.auth.getSession();
  return res;
}

Enter fullscreen mode Exit fullscreen mode

现在在应用程序目录中创建一个名为 auth 的新文件夹,然后在 auth 中创建另一个名为 callback 的文件夹,最后创建一个名为 route.ts 的文件。在该文件中添加以下代码:

// app/auth/callback/route.ts
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

import type { NextRequest } from "next/server";
import type { Database } from "@/lib/database.types";

export async function GET(request: NextRequest) {
  const requestUrl = new URL(request.url);
  const code = requestUrl.searchParams.get("code");

  if (code) {
    const cookieStore = cookies();
    const supabase = createRouteHandlerClient<Database>({
      cookies: () => cookieStore,
    });
    await supabase.auth.exchangeCodeForSession(code);
  }

  // URL to redirect to after sign in process completes
  return NextResponse.redirect(requestUrl.origin);
}

Enter fullscreen mode Exit fullscreen mode

完成上述设置后,我们就可以创建一个登录页面了。在 app 目录中,创建一个名为 login 的新文件夹,其中包含一个 page.tsx 文件。

// app/login/page.tsx
"use client";

import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
import { useRouter } from "next/navigation";
import { useState } from "react";

import type { Database } from "@/lib/database.types";

export default function Login() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const router = useRouter();
  const supabase = createClientComponentClient<Database>();

  const handleSignUp = async () => {
    await supabase.auth.signUp({
      email,
      password,
      options: {
        emailRedirectTo: `${location.origin}/auth/callback`,
      },
    });
    router.refresh();
  };

  const handleSignIn = async () => {
    await supabase.auth.signInWithPassword({
      email,
      password,
    });
    router.refresh();
  };

  const handleSignOut = async () => {
    await supabase.auth.signOut();
    router.refresh();
  };

  return (
    <>
      <input
        name="email"
        onChange={(e) => setEmail(e.target.value)}
        value={email}
      />
      <input
        type="password"
        name="password"
        onChange={(e) => setPassword(e.target.value)}
        value={password}
      />
      <button onClick={handleSignUp}>Sign up</button>
      <button onClick={handleSignIn}>Sign in</button>
      <button onClick={handleSignOut}>Sign out</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

现在在 auth 目录中创建一个名为 sign-up 的新文件夹,并在该文件中创建一个 route.ts 文件。添加以下代码:

// app/auth/sign-up/route.ts
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

import type { Database } from "@/lib/database.types";

export async function POST(request: Request) {
  const requestUrl = new URL(request.url);
  const formData = await request.formData();
  const email = String(formData.get("email"));
  const password = String(formData.get("password"));
  const cookieStore = cookies();
  const supabase = createRouteHandlerClient<Database>({
    cookies: () => cookieStore,
  });

  await supabase.auth.signUp({
    email,
    password,
    options: {
      emailRedirectTo: `${requestUrl.origin}/auth/callback`,
    },
  });

  return NextResponse.redirect(requestUrl.origin, {
    status: 301,
  });
}
Enter fullscreen mode Exit fullscreen mode

在同一位置创建另一个名为 login 的文件夹。

// app/auth/login/route.ts
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

import type { Database } from "@/lib/database.types";

export async function POST(request: Request) {
  const requestUrl = new URL(request.url);
  const formData = await request.formData();
  const email = String(formData.get("email"));
  const password = String(formData.get("password"));
  const cookieStore = cookies();
  const supabase = createRouteHandlerClient<Database>({
    cookies: () => cookieStore,
  });

  await supabase.auth.signInWithPassword({
    email,
    password,
  });

  return NextResponse.redirect(requestUrl.origin, {
    status: 301,
  });
}
Enter fullscreen mode Exit fullscreen mode

最后在同一个地方添加注销路线。

// app/auth/logout/route.ts
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'

import type { Database } from '@/lib/database.types'

export async function POST(request: Request) {
  const requestUrl = new URL(request.url)
  const cookieStore = cookies()
  const supabase = createRouteHandlerClient<Database>({ cookies: () => cookieStore })

  await supabase.auth.signOut()

  return NextResponse.redirect(`${requestUrl.origin}/login`, {
    status: 301,
  })
}
Enter fullscreen mode Exit fullscreen mode

现在,当您导航到 localhost http://localhost:3000/login时,应该有基本的登录注销注册功能

现在,我们为下一个 js 应用程序提供了一些基本样板,其中包含 prisma shadcn 和 supabase 身份验证设置。

文章来源:https://dev.to/isaacdyor/setting-up-nextjs-project-with-prisma-200j
PREV
同步引擎是 Web 应用程序的未来吗?
NEXT
HTTPS 的工作原理