Topics

使用 Supabase×Prisma 连接数据库(使用 Gemini CLI 也没问题!)

  • column

翔先生和Hashi等人正在利用 Supabase 开展一些很有意思的开发项目。与以往的瀑布式开发不同,听说大家是从 UI 开始、一边讨论功能需求一边推进,我也不甘示弱!

因此,本次我们将创建一个简单的备忘录应用,并整理出将 Supabase 与 Prisma 结合、把数据保存到数据库的步骤作为备忘记录。Gemini CLI也一起用上!

环境:Next.js

由于不使用 Supabase 的 Auth、Realtime 和 RLS,而是通过 API 使用数据库功能,因此我们选择 Prisma。它可以将数据库表当作 JavaScript / TypeScript 对象来操作,因此应该可以用前端工程师熟悉的写法来编写代码。

Prisma

Prisma ORM 是一个可用于 TypeScript 和 JavaScript 的 ORM(对象关系映射)库。

简单来说,它是一个工具,可以通过编程语言对象中定义的方法进行数据库操作,无需编写 SQL。

此外,Prisma 会根据数据库的 Schema 自动生成类型,因此在尝试操作数据库中不存在的字段或类型不匹配的数据时,编译错误会帮助你及时发现问题!

Supabase

Supabase 是一款全栈后端服务,作为开源的 Firebase 替代方案备受关注。

  • 基于 PostgreSQL 数据库构建,具备出色的扩展性和可靠性
  • 通过实时数据库功能,数据变更可自动通知
  • 支持多种身份验证方式(电子邮件/密码、社交登录、手机验证等)
  • 借助文件存储功能,可高效管理大容量文件
  • 通过自动生成 RESTful API 和 GraphQL API,实现快速开发
  • 提供丰富的开发者工具和 SDK,配置与部署简便

由于我们很重视 Supabase 的对接流程,界面部分将交由 Gemini CLI 来生成。它的便利程度简直让人惊喜!公司使用 Google Workspace,真是太好了!

我想用 Supabase 和 Prisma 开发一个备忘录功能。请先帮我创建 page.tsx。

好的,那么请实现这样一个功能:点击 Save Memo 按钮后,将 textarea 中输入的内容以列表形式添加到 Your Memos 中。

外观看起来没问题!输入的内容已经会添加到下方的列表中。不过由于只是通过 JavaScript 处理事件,刷新页面后列表会消失。

要保留输入的内容,需要使用数据库!

首先,在 Supabase 端创建新项目。

通过“New Project”按钮创建项目。Database password 在通过 Prisma 建立连接时需要用到,请妥善记录。

接下来连接 Prisma。

npm install prisma --save-dev

npx prisma init

输入这些命令后,将安装 prisma,并生成 prisma 文件夹。

将 .env 文件中的 DATABASE_URL 替换为 Supabase 的连接 URL。将 [YOUR-PASSWORD] 部分替换为前面在 Supabase 中设置的 Database password。

DATABASE_URL="postgresql://postgres.odoaingwnxkhkeujwhtp:[YOUR-PASSWORD]@aws-1-ap-northeast-1.pooler.supabase.com:5432/postgres"

然后在 /prisma/schema.prisma 中编写 schema(数据库结构定义)。

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

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

model Memo {
  id        Int      @id @default(autoincrement())
  content   String
  createdAt DateTime @default(now())
}

model ○○ 可以任意命名。

编写完 schema 后,执行迁移(migration)。

npx prisma migrate dev --name init

将生成 migration.sql 文件,并在 Supabase 端创建数据表。

创建用于连接 Supabase 的 API

为了将输入的值保存到 Supabase,我们将创建用于连接的 API。

在项目中安装 Prisma Client。

npm install @prisma/client

连接 Supabase

/libs/prisma.ts

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

const Prisma = new PrismaClient(); //インスタンス化

export const main = async () => {
  try {
    await Prisma.$connect();
  } catch (error) {
    return Error("DB接続に失敗しました");
  }
};

API

/api/memos/route.ts

import { NextResponse } from "next/server";
import { prisma } from '@/lib/prisma';

export const GET = async (req: Request, res: NextResponse) => {
  try {
    await main();             
    const memos = await Prisma.memo.findMany(); 
    return NextResponse.json(memos);
  } catch (error) {
    return NextResponse.json("エラーが発生しました");
  } finally {  
    await Prisma.$disconnect();
  }
};

export const POST = async (req: Request, res: NextResponse) => {
  const { content } = await req.json(); 
  try {
    await main();
    const memos = await Prisma.memo.**create**({
      data: {
        content: content,
      },
    });
    return NextResponse.json(memos);
  } catch (error) {
    return NextResponse.json("エラーが発生しました");
  } finally {
    await Prisma.$disconnect();
  }
};

GET 函数是用于获取数据库全部数据的 API,通过 findMany() 方法获取符合条件的所有记录。Prisma.memo.findMany() 中的 "memo" 是 schema 名称。

POST 函数是向数据库添加数据的 API,通过 create() 方法进行创建。

此外还提供删除、更新等方法,即使不会编写 SQL 语句,也可以凭借 JavaScript 的思路进行操作。

至此,基本准备工作已经完成。

接下来只需将前面编写的 API 复制到 page.tsx 中并完成集成即可。

'use client';

import React, { useState, useEffect } from 'react';

interface Memo {
  id: number;
  content: string;
  createdAt: string;
}

export default function MemoApp() {
  const [memos, setMemos] = useState<Memo[]>([]);
  const [newMemo, setNewMemo] = useState('');

  const fetchMemos = async () => {
    const response = await fetch('/api/memos');
    const data = await response.json();
    setMemos(data);
  };

  useEffect(() => {
    fetchMemos();
  }, []);

  const handleSaveMemo = async (e: React.FormEvent) => {
    e.preventDefault();
    if (newMemo.trim() !== '') {
      await fetch('/api/memos', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ content: newMemo }),
      });
      setNewMemo('');
      fetchMemos();
    }
  };

  return (
    <div className="bg-white min-h-screen">
      <div className="max-w-2xl mx-auto px-4 py-16">
        <header className="text-center mb-12">
          <h1 className="text-5xl font-extrabold text-gray-900">Memo</h1>
        </header>

        <main>
          <div className="mb-12">
            <form onSubmit={handleSaveMemo}>
              <textarea
                className="w-full p-4 text-gray-800 bg-gray-100 rounded-lg border-2 border-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition"
                placeholder="Create a new memo..."
                rows={3}
                value={newMemo}
                onChange={(e) => setNewMemo(e.target.value)}
              ></textarea>
              <div className="flex justify-end mt-4">
                <button
                  type="submit"
                  className="px-6 py-2 bg-gray-800 text-white font-semibold rounded-lg hover:bg-gray-900 transition"
                >
                  Save Memo
                </button>
              </div>
            </form>
          </div>

          <section>
            <h2 className="text-3xl font-bold text-gray-800 mb-6">Your Memos</h2>
            <div className="space-y-4">
              {memos.length > 0 ? (
                memos.map((memo) => (
                  <div key={memo.id} className="bg-gray-50 p-6 rounded-lg shadow-sm">
                    <p className="text-gray-700">{memo.content}</p>
                  </div>
                ))
              ) : (
                <div className="text-center text-gray-500">
                  <p>No memos yet. Add one above!</p>
                </div>
              )}
            </div>
          </section>
        </main>
      </div>
    </div>
  );
}

数据已保存在 Supabase 中!

这次我们借助 Gemini 粗略地完成了创建,但如果能利用数据库保存数据,就能拓展可开发的功能和应用的范围!

原本以为后端好像很难……但用 GUI 工具就能管理数据库,非常直观,数据库对接的门槛也降低了!

太好了!

撰写本文的人

主要从事标记(Markup)工作,并使用 JavaScript、React 和 Next.js 进行前端开发。看到自己参与的网站顺利上线,我会非常开心!爱好是弹吉他。喜欢猫和烤红薯🐱🍠

Hirappi

前端工程师 / 2022年入职

查看此员工的文章

以稳健的团队体制和迅速的应对能力为傲

Liberogic 的资深团队积极推动项目,因此深受客户好评。
我们致力于合理配置项目经理与总监,确保整个项目顺利推进。 我们采用按需投入的方式,避免不必要的全员投入导致成本增加,合理分配资源、人尽其才。同时,我们在掌握业务内容、制作与提交报价方面的速度也广受好评。

※ 当社不积极开展 SES 类驻场业务,敬请谅解。

您可以使用 Slack、Teams、Redmine、Backlog、Asana、Jira、Notion、Google Workspace、Zoom、Webex 等几乎所有主流的项目管理工具和聊天工具。

在利用 SES 或离岸外包的大型项目中,您是否正在为技术难题或推进方式而烦恼?

案例分析