Skip to content

Supabase 和 Astro

Supabase 是一个开源 Firebase 替代品。它提供 Postgres 数据库、身份验证、边缘功能、实时订阅和存储。

¥Supabase is an open source Firebase alternative. It provides a Postgres database, authentication, edge functions, realtime subscriptions, and storage.

在 Astro 中初始化 Supabase

Section titled 在 Astro 中初始化 Supabase

¥Initializing Supabase in Astro

¥Prerequisites

  • Supabase 项目。如果你没有,你可以在 supabase.com 免费注册并创建一个新项目。

  • 启用了 服务器端渲染(SSR) 的 Astro 项目。

  • 你的项目的 Supabase 凭据。你可以在 Supabase 项目的设置 > API 选项卡中找到这些内容。

    • SUPABASE_URL:你的 Supabase 项目的 URL。

    • SUPABASE_ANON_KEY:Supabase 项目的匿名密钥。

¥Adding Supabase credentials

要将 Supabase 凭据添加到 Astro 项目,请将以下内容添加到 .env 文件中:

¥To add your Supabase credentials to your Astro project, add the following to your .env file:

.env
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY

现在,这些环境变量在你的项目中可用。

¥Now, these environment variables are available in your project.

如果你希望环境变量具有 IntelliSense,请在 src/ 目录中编辑或创建 env.d.ts 并添加以下内容:

¥If you would like to have IntelliSense for your environment variables, edit or create the env.d.ts in your src/ directory and add the following:

src/env.d.ts
interface ImportMetaEnv {
readonly SUPABASE_URL: string
readonly SUPABASE_ANON_KEY: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

你的项目现在应该包含以下文件:

¥Your project should now include these files:

  • Directorysrc/
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

¥Installing dependencies

要连接到 Supabase,你需要在项目中安装 @supabase/supabase-js

¥To connect to Supabase, you will need to install @supabase/supabase-js in your project.

Terminal window
npm install @supabase/supabase-js

接下来,在 src/ 目录中创建一个名为 lib 的文件夹。你将在此处添加 Supabase 客户端。

¥Next, create a folder named lib in your src/ directory. This is where you will add your Supabase client.

supabase.ts 中,添加以下内容来初始化你的 Supabase 客户端:

¥In supabase.ts, add the following to initialize your Supabase client:

src/lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
);

现在,你的项目应该包含以下文件:

¥Now, your project should include these files:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

使用 Supabase 添加身份验证

Section titled 使用 Supabase 添加身份验证

¥Adding authentication with Supabase

Supabase 提供开箱即用的身份验证。它支持电子邮件/密码身份验证和 OAuth 身份验证,包括 GitHub、Google 等许多提供商。

¥Supabase provides authentication out of the box. It supports email/password authentication and OAuth authentication with many providers including GitHub, Google, and several others.

¥Prerequisites

  • Astro 项目 使用 Supabase 初始化

  • 启用电子邮件/密码身份验证的 Supabase 项目。你可以在 Supabase 项目的身份验证 > 提供程序选项卡中启用此功能。

创建身份验证服务器端点

Section titled 创建身份验证服务器端点

¥Creating auth server endpoints

要向你的项目添加身份验证,你将需要创建一些服务器端点。这些端点将用于注册、登录和注销用户。

¥To add authentication to your project, you will need to create a few server endpoints. These endpoints will be used to register, sign in, and sign out users.

  • POST /api/auth/register:注册新用户。

  • POST /api/auth/signin:登录用户。

  • GET /api/auth/signout:注销用户。

在项目的 src/pages/api/auth 目录中创建这些端点。如果你使用 hybrid 渲染模式,则必须在每个文件的顶部指定 export const prerender = false 以按需渲染这些端点。你的项目现在应该包含这些新文件:

¥Create these endpoints in the src/pages/api/auth directory of your project. If you are using hybrid rendering mode, you must specify export const prerender = false at the top of each file to render these endpoints on demand. Your project should now include these new files:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • Directorypages/
      • Directoryapi/
        • Directoryauth/
          • signin.ts
          • signout.ts
          • register.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

register.ts 在 Supabase 中创建一个新用户。它接受带有电子邮件和密码的 POST 请求。然后,它使用 Supabase SDK 创建新用户。

¥register.ts creates a new user in Supabase. It accepts a POST request with the an email and password. It then uses the Supabase SDK to create a new user.

src/pages/api/auth/register.ts
// With `output: 'hybrid'` configured:
// export const prerender = false;
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return new Response("Email and password are required", { status: 400 });
}
const { error } = await supabase.auth.signUp({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
return redirect("/signin");
};

signin.ts 登录用户。它接受带有电子邮件和密码的 POST 请求。然后,它使用 Supabase SDK 登录用户。

¥signin.ts signs in a user. It accepts a POST request with the an email and password. It then uses the Supabase SDK to sign in the user.

src/pages/api/auth/signin.ts
// With `output: 'hybrid'` configured:
// export const prerender = false;
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return new Response("Email and password are required", { status: 400 });
}
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, {
path: "/",
});
cookies.set("sb-refresh-token", refresh_token, {
path: "/",
});
return redirect("/dashboard");
};

signout.ts 注销用户。它接受 GET 请求并删除用户的访问和刷新令牌。

¥signout.ts signs out a user. It accepts a GET request and removes the user’s access and refresh tokens.

src/pages/api/auth/signout.ts
// With `output: 'hybrid'` configured:
// export const prerender = false;
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ cookies, redirect }) => {
cookies.delete("sb-access-token", { path: "/" });
cookies.delete("sb-refresh-token", { path: "/" });
return redirect("/signin");
};

¥Creating auth pages

现在你已经创建了服务器端点,接下来创建将使用它们的页面。

¥Now that you have created your server endpoints, create the pages that will use them.

  • src/pages/register:包含一个用于注册新用户的表格。

  • src/pages/signin:包含用于登录用户的表单。

  • src/pages/dashboard:包含只有经过身份验证的用户才能访问的页面。

src/pages 目录中创建这些页面。你的项目现在应该包含这些新文件:

¥Create these pages in the src/pages directory. Your project should now include these new files:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • Directorypages/
      • Directoryapi/
        • Directoryauth/
          • signin.ts
          • signout.ts
          • register.ts
      • register.astro
      • signin.astro
      • dashboard.astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

register.astro 包含一个用于注册新用户的表格。它接受电子邮件和密码并向 /api/auth/register 发送 POST 请求。

¥register.astro contains a form to register a new user. It accepts an email and password and sends a POST request to /api/auth/register.

src/pages/register.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Register">
<h1>Register</h1>
<p>Already have an account? <a href="/signin">Sign in</a></p>
<form action="/api/auth/register" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Register</button>
</form>
</Layout>

signin.astro 包含用于登录用户的表单。它接受电子邮件和密码并向 /api/auth/signin 发送 POST 请求。它还检查访问令牌和刷新令牌是否存在。如果它们存在,它将重定向到仪表板。

¥signin.astro contains a form to sign in a user. It accepts an email and password and sends a POST request to /api/auth/signin. It also checks for the presence of the access and refresh tokens. If they are present, it redirects to the dashboard.

src/pages/signin.astro
---
import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) {
return redirect("/dashboard");
}
---
<Layout title="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
</form>
</Layout>

dashboard.astro 包含一个只有经过身份验证的用户才能访问的页面。它检查访问令牌和刷新令牌是否存在。如果它们不存在或无效,则会重定向到登录页面。

¥dashboard.astro contains a page that is only accessible to authenticated users. It checks for the presence of the access and refresh tokens. If they are not present or are invalid, it redirects to the sign in page.

src/pages/dashboard.astro
---
import Layout from "../layouts/Layout.astro";
import { supabase } from "../lib/supabase";
const accessToken = Astro.cookies.get("sb-access-token");
const refreshToken = Astro.cookies.get("sb-refresh-token");
if (!accessToken || !refreshToken) {
return Astro.redirect("/signin");
}
let session;
try {
session = await supabase.auth.setSession({
refresh_token: refreshToken.value,
access_token: accessToken.value,
});
if (session.error) {
Astro.cookies.delete("sb-access-token", {
path: "/",
});
Astro.cookies.delete("sb-refresh-token", {
path: "/",
});
return Astro.redirect("/signin");
}
} catch (error) {
Astro.cookies.delete("sb-access-token", {
path: "/",
});
Astro.cookies.delete("sb-refresh-token", {
path: "/",
});
return Astro.redirect("/signin");
}
const email = session.data.user?.email;
---
<Layout title="dashboard">
<h1>Welcome {email}</h1>
<p>We are happy to see you here</p>
<form action="/api/auth/signout">
<button type="submit">Sign out</button>
</form>
</Layout>

¥Adding OAuth authentication

要将 OAuth 身份验证添加到你的项目,你将需要编辑 Supabase 客户端以启用 "pkce" 的身份验证流程。你可以在 Supabase 文档 中阅读有关身份验证流程的更多信息。

¥To add OAuth authentication to your project, you will need to edit your Supabase client to enable authentication flow with "pkce". You can read more about authentication flows in the Supabase documentation.

src/lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
{
auth: {
flowType: "pkce",
},
},
);

接下来,在 Supabase 仪表板中,启用你想要使用的 OAuth 提供程序。你可以在 Supabase 项目的身份验证 > 提供商选项卡中找到受支持的提供商列表。

¥Next, in the Supabase dashboard, enable the OAuth provider you would like to use. You can find the list of supported providers in the Authentication > Providers tab of your Supabase project.

以下示例使用 GitHub 作为 OAuth 提供程序。要将你的项目连接到 GitHub,请按照 Supabase 文档

¥The following example uses GitHub as the OAuth provider. To connect your project to GitHub, follow the steps in the Supabase documentation.

然后,创建一个新的服务器端点来处理 src/pages/api/auth/callback.ts 处的 OAuth 回调。此端点将用于交换 OAuth 代码以获取访问和刷新令牌。

¥Then, create a new server endpoint to handle the OAuth callback at src/pages/api/auth/callback.ts. This endpoint will be used to exchange the OAuth code for an access and refresh token.

src/pages/api/auth/callback.ts
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const GET: APIRoute = async ({ url, cookies, redirect }) => {
const authCode = url.searchParams.get("code");
if (!authCode) {
return new Response("No code provided", { status: 400 });
}
const { data, error } = await supabase.auth.exchangeCodeForSession(authCode);
if (error) {
return new Response(error.message, { status: 500 });
}
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, {
path: "/",
});
cookies.set("sb-refresh-token", refresh_token, {
path: "/",
});
return redirect("/dashboard");
};

接下来,编辑登录页面以包含用于使用 OAuth 提供商登录的新按钮。此按钮应向 /api/auth/signin 发送 POST 请求,并将 provider 设置为 OAuth 提供商的名称。

¥Next, edit the sign in page to include a new button to sign in with the OAuth provider. This button should send a POST request to /api/auth/signin with the provider set to the name of the OAuth provider.

src/pages/signin.astro
---
import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) {
return redirect("/dashboard");
}
---
<Layout title="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
<button value="github" name="provider" type="submit">Sign in with GitHub</button>
</form>
</Layout>

最后,编辑登录服务器端点以处理 OAuth 提供程序。如果 provider 存在,它将重定向到 OAuth 提供商。否则,它将使用电子邮件和密码登录用户。

¥Finally, edit the sign in server endpoint to handle the OAuth provider. If the provider is present, it will redirect to the OAuth provider. Otherwise, it will sign in the user with the email and password.

src/pages/api/auth/signin.ts
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
import type { Provider } from "@supabase/supabase-js";
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
const provider = formData.get("provider")?.toString();
const validProviders = ["google", "github", "discord"];
if (provider && validProviders.includes(provider)) {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: provider as Provider,
options: {
redirectTo: "http://localhost:4321/api/auth/callback"
},
});
if (error) {
return new Response(error.message, { status: 500 });
}
return redirect(data.url);
}
if (!email || !password) {
return new Response("Email and password are required", { status: 400 });
}
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, {
path: "/",
});
cookies.set("sb-refresh-token", refresh_token, {
path: "/",
});
return redirect("/dashboard");
};

创建 OAuth 回调端点并编辑登录页面和服务器端点后,你的项目应具有以下文件结构:

¥After creating the OAuth callback endpoint and editing the sign in page and server endpoint, your project should have the following file structure:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • Directorypages/
      • Directoryapi/
        • Directoryauth/
          • signin.ts
          • signout.ts
          • register.ts
          • callback.ts
      • register.astro
      • signin.astro
      • dashboard.astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

¥Community Resources

More backend service guides

Astro 中文网 - 粤ICP备13048890号