Drizzle 与 Supabase 边缘函数的结合

本教程演示了如何在 Supabase Edge 函数 中使用 Drizzle ORM。

This guide assumes familiarity with:
  • 你应该已经安装了最新版本的 Supabase CLI

  • 你应该已经安装了 Drizzle ORM 和 Drizzle 套件。你可以通过运行以下命令来实现:

npm
yarn
pnpm
bun
npm i drizzle-orm -D drizzle-kit
  • 你应该已经安装了 Docker Desktop。它是本地开发的先决条件。请按照官方 docs 进行安装。

要了解如何在本地计算机上创建并部署基本 Edge 函数,请参阅 Edge Functions 快速入门

创建表

src 目录中创建一个 schema.ts 文件并声明表模式:

src/schema.ts
import { pgTable, serial, text, integer } from "drizzle-orm/pg-core";

export const usersTable = pgTable('users_table', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  age: integer('age').notNull()
})

此文件将用于生成数据库的迁移。

设置 Drizzle 配置文件

Drizzle 配置 - Drizzle 套件 使用的配置文件,包含有关数据库连接、迁移文件夹和模式文件的所有信息。

在项目根目录中创建一个 drizzle.config.ts 文件并添加以下内容:

drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./src/schema.ts",
  out: "./supabase/migrations",
  dialect: "postgresql",
});

在本教程中,我们将使用 Drizzle 工具包为我们的架构生成迁移。

初始化新的 Supabase 项目

在本地计算机上的文件夹中创建一个新的 Supabase 项目:

supabase init

它将创建包含 config.toml 文件的 supabase 文件夹:

└── supabase
    └── config.toml

如果你使用的是 Visual Studio Code,请按照 Supabase 文档 的步骤设置 Deno。

生成迁移

运行 drizzle-kit generate 命令生成迁移:

npx drizzle-kit generate

它将在 supabase/migrations 目录中创建一个新的迁移文件:

应用迁移

要启动 Supabase 本地开发堆栈,请运行以下命令:

supabase start

要应用迁移,请运行以下命令:

supabase migration up

你可以在 documentation 中阅读更多关于 Supabase 迁移的内容。

IMPORTANT
Don’t forget to run Docker

或者,你可以使用 drizzle-kit migrate 命令应用迁移。在 documentation 中了解更多关于此迁移过程的信息。

创建新的 Edge 函数

运行 supabase functions new [FUNCTION_NAME] 命令创建一个新的 Edge 函数:

supabase functions new drizzle-tutorial

它将在 supabase/functions 目录中创建一个包含函数名称的新文件夹:

└── supabase
    └── functions
    │   └── drizzle-tutorial
    │   │   ├── .npmrc ## Function-specific npm configuration (if needed)
    │   │   ├── deno.json ## Function-specific Deno configuration
    │   │   └── index.ts ## Your function code

创建新的 Edge Function 时,它将默认使用 TypeScript。但是,可以用 JavaScript 编写 Edge Function。在 documentation 中了解更多信息。

设置导入

将以下导入语句添加到 supabase/functions/drizzle-tutorial 目录中的 deno.json 文件中:

supabase/functions/drizzle-tutorial/deno.json
{
  "imports": {
    "drizzle-orm/": "npm:/drizzle-orm/",
    "postgres": "npm:postgres"
  }
}

你可以阅读更多关于管理依赖的内容 此处

将你的架构复制到函数目录

将边缘函数中使用的代码从 src/schema.ts 文件复制到 supabase/functions/drizzle-tutorial/index.ts 文件:

supabase/functions/drizzle-tutorial/index.ts
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { pgTable, serial, text, integer } from "drizzle-orm/pg-core";

const usersTable = pgTable('users_table', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  age: integer('age').notNull()
})

Deno.serve(async (req) => {
  const { name } = await req.json()
  const data = {
    message: `Hello ${name}!`,
  }

  return new Response(
    JSON.stringify(data),
    { headers: { "Content-Type": "application/json" } },
  )
})  
IMPORTANT

在 Deno 生态系统中,每个函数都应被视为一个独立的项目,拥有自己的一组依赖和配置。出于这些原因,Supabase 建议在每个函数目录中维护单独的配置文件(deno.json.npmrcimport_map.json),即使这意味着重复一些配置。阅读更多 此处

将 Drizzle ORM 连接到你的数据库

使用数据库配置更新你的边缘函数代码:

supabase/functions/drizzle-tutorial/index.ts
// Setup type definitions for built-in Supabase Runtime APIs
import { integer, pgTable, serial, text } from "drizzle-orm/pg-core";
import { drizzle } from "drizzle-orm/postgres-js";
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import postgres from "postgres";

const usersTable = pgTable('users_table', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  age: integer('age').notNull()
})

Deno.serve(async () => {
  const connectionString = Deno.env.get("SUPABASE_DB_URL")!;

  // Disable prefetch as it is not supported for "Transaction" pool mode
  const client = postgres(connectionString, { prepare: false });
  const db = drizzle({ client });

  await db.insert(usersTable).values({
    name: "Alice",
    age: 25
  })
  const data = await db.select().from(usersTable);

  return new Response(
    JSON.stringify(data)
  )
})

SUPABASE_DB_URL 是直接数据库连接的默认环境变量。在 documentation 中了解更多关于在 Supabase Edge Functions 中管理环境变量的信息。

本地测试你的代码

运行以下命令在本地测试你的函数:

supabase functions serve --no-verify-jwt

在浏览器中导航到路由 (e.g. /drizzle-tutorial)

[
  {
    "id": 1,
    "name": "Alice",
    "age": 25
  }
]

将本地项目链接到托管的 Supabase 项目

你可以在 dashboard 中创建新的 Supabase 项目,也可以按照 link 的步骤操作。

从项目设置中复制 Reference ID 文件,并通过运行以下命令将本地开发项目链接到托管的 Supabase 项目:

supabase link --project-ref=<REFERENCE_ID>

通过运行以下命令将架构更改推送到托管的 Supabase 项目:

supabase db push

设置环境变量变量

你可以通过点击仪表板顶部栏中的“连接”找到 Project connect details,并从 Transaction pooler 部分复制 URI。请记住将密码占位符替换为你的实际数据库密码。

documentation 中了解更多关于连接池的信息。

更新你的边缘函数代码,以使用 DATABASE_URL 环境变量而不是 SUPABASE_DB_URL

supabase/functions/drizzle-tutorial/index.ts
// imports

// const connectionString = Deno.env.get("SUPABASE_DB_URL")!;
const connectionString = Deno.env.get("DATABASE_URL")!;

// code

运行以下命令设置环境变量:

supabase secrets set DATABASE_URL=<CONNECTION_STRING>

documentation 中了解更多关于在 Supabase Edge Functions 中管理环境变量的信息。

部署你的函数

运行以下命令部署你的函数:

supabase functions deploy drizzle-tutorial --no-verify-jwt

最后,你可以使用已部署项目的 URL 并导航到你创建的路由 (e.g. /drizzle-tutorial) 来访问你的边缘函数。