Supabase 是一个开源的 Firebase 替代方案。它使用企业级的开源工具来构建 Firebase 的功能。目前在 GitHub 上斩获68.8K的 star,可以说是非常火爆了。
它由 Postgres 数据库和 REST API、GraphQL API、实时订阅、函数、文件存储等功能组成。将了这么一大堆,其实就是一个后端服务,同时他提供了一个 SAAS 服务,可以帮助我们快速搭建一个应用的后端服务。最性感的,还是他有免费的额度,可以创建 2 个数据库应用,而且还自带认证功能,这对于一个拿来验证的小项目来说,已经足够用了。
• 托管的 Postgres 数据库
• 身份验证和授权
• 自动生成的 API
• REST API
• GraphQL API
• 实时订阅
• 函数
• 数据库函数
• Edge 函数
• 文件存储
• AI + 向量/嵌入工具包
比较惊喜的是,他最近也提供了向量数据库的功能。先埋个点,以后肯定是可以用得上的。
实际上,初期,我们用得比较多的就是数据库和身份验证这两个功能。今天,我就来带你用 Supabase 来搞定认证。请注意,只需要 1 分钟,就可以搞定接入认证,真的灰常简单。
首先,我们需要注册一个 Supabase 账号,然后创建一个项目。
然后就可以到这个链接下面 https://supabase.com/dashboard/project/xxxxx/settings/api 看到这个项目的 API URL 和公钥、私钥了。私钥这个一定得是在服务端跑,比如使用 Next.js 的 API 路由,这样才能保证私钥不会泄露。
superbase 的身份验证功能,支持的方式有下图这么多种,可以看到支持 GitHub等方式,我们可以根据自己的需求来选择。
我尝试的最为简单的方式就是使用邮箱加密码的方式来注册和登录。下面是我在 Next.js 中的实现方式。
// Sign up with email
const { user, error } = await supabase.auth.signUp({
email: 'example@email.com',
password: 'example-password',
})
// Sign in with email
const { user, error } = await supabase.auth.signIn({
email: 'example@email.com',
password: 'example-password',
})
你可能惊讶,就这就完了,确实就这样就完了,简单的写下前端页面,然后调用 Supabase 的 API,就可以实现注册和登录了。一旦注册完成,数据库 auth.users 表中就会写一个记录。
// app/actions.ts
'use server'
import { redirect } from 'next/navigation'
import { createClient } from '@/lib/supabaseClient'
export async function createUser(formData: FormData) {
const supabase = createClient()
const email = formData.get('email')
const password = formData.get('password')
const { user, error } = await supabase.auth.signUp({
email,
password,
})
if (error) {
console.error('Error:', error.message)
return redirect('/error')
}
await supbase.auth.signInWithPassword({
email,
password,
})
redirect('/dashboard')
}
// app/login/page.tsx
import { createTodo } from './actions'
export default function Page() {
return (
<form action={createUser}>
<input type="email" name="email" />
<input type="password" name="password" />
<button type="submit">Sign Up</button>
</form>
)
}
你可能会说,那我下次请求的时候,服务端是怎么认识我的呢?这个时候,我们可以使用 cookie 来保存用户的信息,然后在服务端通过 cookie 来识别用户。其实,我们 createClient 的时候,就将 cookie 传递给了服务端,这样就可以在服务端通过 cookie 来识别用户了。
// lib/supabaseClient.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export function createClient() {
const cookieStore = cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_SERVICE_ROLE_KEY,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {
}
},
},
}
)
}
所以,这一切都是 Supabase 帮你做完了,这就对于咱们的认知负担很低了,只需要关注业务逻辑就可以了。完全可以理解为认证就是调用一个 API,然后就可以拿到用户的信息了。
在实际的业务中,我们可以将 public 下面的库表,比如自定一定的 User 表,然后将 auth.users 表和 User 表进行关联,这样,就可以配合 Supabase 的权限系统,来实现对用户的权限控制了。
create table users (
id uuid primary key references auth.users not null,,
name text,
age int,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
Supabase 的权限系统,可以通过设置 Row Level Security 来实现对用户的权限控制。举一个例子,只允许编辑自己发布的文章。
假设 posts 表设计如下:
create table posts (
post_id uuid primary key default gen_random_uuid(),
user_id uuid references users(id) not null,
title text,
content text,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
create policy "Allow users to update their own posts" on posts for update using (auth.uid() = user_id);
你代码都不用写,Supabase 就帮你做完了权限的管理,这就是 Supabase 的强大之处。
另外说点别的,Supabase 还提供了文件存储的能力,这个功能也是非常实用的。比如,我们可以将用户上传的文件存储到 Supabase 的文件存储中。
// Upload a file
import { createClient } from '@supabase/supabase-js'
// Create Supabase client
const supabase = createClient('your_project_url', 'your_supabase_api_key')
// Upload file using standard upload
async function uploadFile(file) {
const { data, error } = await supabase.storage.from('bucket_name').upload('file_path', file)
if (error) {
// Handle error
} else {
// Handle success
}
}
这些都是有免费的额度的,另外刚才说的 向量数据库,也是免费的,可以用来做RAG,简直是太香了。
const session = new Supabase.ai.Session('gte-small');
Deno.serve(async (req) => {
// Extract input string from JSON body
const { input } = await req.json();
// Generate the embedding from the user input
const embedding = await session.run(input, {
mean_pool: true,
normalize: true,
});
// Return the embedding
return new Response(
JSON.stringify({ embedding }),
{ headers: { 'Content-Type': 'application/json' } }
);
});