Next.js Supabase V3 (#463)

Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
This commit is contained in:
Giancarlo Buomprisco
2026-03-24 13:40:38 +08:00
committed by GitHub
parent 4912e402a3
commit 7ebff31475
840 changed files with 71395 additions and 20095 deletions

View File

@@ -0,0 +1,341 @@
---
status: "published"
label: "Database Functions"
order: 3
title: "PostgreSQL Database Functions for Multi-Tenant SaaS"
description: "Use built-in database functions for permissions, roles, subscriptions, and MFA checks. Includes has_permission, is_account_owner, has_active_subscription, and more."
---
Makerkit includes built-in PostgreSQL functions for common multi-tenant operations like permission checks, role verification, and subscription status. Use these functions in RLS policies and application code to enforce consistent security rules across your database.
{% sequence title="Database Functions Reference" description="Built-in functions for multi-tenant operations" %}
[Call functions from SQL and RPC](#calling-database-functions)
[Account ownership and membership](#account-functions)
[Permission checks](#permission-functions)
[Subscription and billing](#subscription-functions)
[MFA and authentication](#authentication-functions)
{% /sequence %}
## Calling Database Functions
### From SQL (RLS Policies)
Use functions directly in SQL schemas and RLS policies:
```sql
-- In an RLS policy
create policy "Users can view their projects"
on public.projects
for select
using (
public.has_role_on_account(account_id)
);
```
### From Application Code (RPC)
Call functions via Supabase RPC:
```tsx
const { data: isOwner, error } = await supabase.rpc('is_account_owner', {
account_id: accountId,
});
if (isOwner) {
// User owns this account
}
```
## Account Functions
### is_account_owner
Check if the current user owns an account. Returns `true` if the account is the user's personal account or if they created a team account.
```sql
public.is_account_owner(account_id uuid) returns boolean
```
**Use cases:**
- Restrict account deletion to owners
- Gate billing management
- Control team settings access
**Example RLS:**
```sql
create policy "Only owners can delete accounts"
on public.accounts
for delete
using (public.is_account_owner(id));
```
### has_role_on_account
Check if the current user has membership on an account, optionally with a specific role.
```sql
public.has_role_on_account(
account_id uuid,
account_role varchar(50) default null
) returns boolean
```
**Parameters:**
- `account_id`: The account to check
- `account_role`: Optional role name (e.g., `'owner'`, `'member'`). If omitted, returns `true` for any membership.
**Example RLS:**
```sql
-- Any member can view
create policy "Members can view projects"
on public.projects
for select
using (public.has_role_on_account(account_id));
-- Only owners can update
create policy "Owners can update projects"
on public.projects
for update
using (public.has_role_on_account(account_id, 'owner'));
```
### is_team_member
Check if a specific user is a member of a team account.
```sql
public.is_team_member(
account_id uuid,
user_id uuid
) returns boolean
```
**Use case:** Verify team membership when the current user context isn't available.
### can_action_account_member
Check if the current user can perform actions on another team member (remove, change role, etc.).
```sql
public.can_action_account_member(
target_team_account_id uuid,
target_user_id uuid
) returns boolean
```
**Logic:**
1. If current user is account owner: `true`
2. If target user is account owner: `false`
3. Otherwise: Compare role hierarchy levels
**Example:**
```tsx
const { data: canRemove } = await supabase.rpc('can_action_account_member', {
target_team_account_id: teamId,
target_user_id: memberId,
});
if (!canRemove) {
throw new Error('Cannot remove a user with equal or higher role');
}
```
## Permission Functions
### has_permission
Check if a user has a specific permission on an account. This is the primary function for granular access control.
```sql
public.has_permission(
user_id uuid,
account_id uuid,
permission_name app_permissions
) returns boolean
```
**Parameters:**
- `user_id`: The user to check (use `auth.uid()` for current user)
- `account_id`: The account context
- `permission_name`: A value from the `app_permissions` enum
**Default permissions:**
```sql
create type public.app_permissions as enum(
'roles.manage',
'billing.manage',
'settings.manage',
'members.manage',
'invites.manage'
);
```
**Example RLS:**
```sql
create policy "Users with tasks.write can insert tasks"
on public.tasks
for insert
with check (
public.has_permission(auth.uid(), account_id, 'tasks.write'::app_permissions)
);
```
**Example RPC:**
```tsx
async function checkTaskWritePermission(accountId: string) {
const { data: hasPermission } = await supabase.rpc('has_permission', {
user_id: (await supabase.auth.getUser()).data.user?.id,
account_id: accountId,
permission: 'tasks.write',
});
return hasPermission;
}
```
See [Permissions and Roles](/docs/next-supabase-turbo/development/permissions-and-roles) for adding custom permissions.
## Subscription Functions
### has_active_subscription
Check if an account has an active or trialing subscription.
```sql
public.has_active_subscription(account_id uuid) returns boolean
```
**Returns `true` when:**
- Subscription status is `active`
- Subscription status is `trialing`
**Returns `false` when:**
- No subscription exists
- Status is `canceled`, `past_due`, `unpaid`, `incomplete`, etc.
**Example RLS:**
```sql
create policy "Only paid accounts can create projects"
on public.projects
for insert
with check (
public.has_active_subscription(account_id)
);
```
**Example application code:**
```tsx
const { data: isPaid } = await supabase.rpc('has_active_subscription', {
account_id: accountId,
});
if (!isPaid) {
redirect('/pricing');
}
```
## Authentication Functions
### is_super_admin
Check if the current user is a super admin. Requires:
- User is authenticated
- User has `super_admin` role
- User is currently signed in with MFA (AAL2)
```sql
public.is_super_admin() returns boolean
```
**Example RLS:**
```sql
create policy "Super admins can view all accounts"
on public.accounts
for select
using (public.is_super_admin());
```
### is_mfa_compliant
Check if the current user meets MFA requirements. Returns `true` when:
- User enabled MFA and is signed in with MFA (AAL2)
- User has not enabled MFA (AAL1 is sufficient)
```sql
public.is_mfa_compliant() returns boolean
```
**Use case:** Allow users without MFA to continue normally while enforcing MFA for users who enabled it.
### is_aal2
Check if the current user is signed in with MFA (AAL2 authentication level).
```sql
public.is_aal2() returns boolean
```
**Use case:** Require MFA for sensitive operations regardless of user settings.
**Example:**
```sql
-- Require MFA for billing operations
create policy "MFA required for billing changes"
on public.billing_settings
for all
using (public.is_aal2());
```
## Configuration Functions
### is_set
Check if a configuration value is set in the `public.config` table.
```sql
public.is_set(field_name text) returns boolean
```
**Example:**
```sql
-- Check if a feature flag is enabled
select public.is_set('enable_new_dashboard');
```
## Function Reference Table
| Function | Purpose | Common Use |
|----------|---------|------------|
| `is_account_owner(account_id)` | Check account ownership | Delete, billing access |
| `has_role_on_account(account_id, role?)` | Check membership/role | View, edit access |
| `is_team_member(account_id, user_id)` | Check specific user membership | Team operations |
| `can_action_account_member(account_id, user_id)` | Check member management rights | Remove, role change |
| `has_permission(user_id, account_id, permission)` | Check granular permission | Feature access |
| `has_active_subscription(account_id)` | Check billing status | Paid features |
| `is_super_admin()` | Check admin status | Admin operations |
| `is_mfa_compliant()` | Check MFA compliance | Security policies |
| `is_aal2()` | Check MFA authentication | Sensitive operations |
## Related Resources
- [Permissions and Roles](/docs/next-supabase-turbo/development/permissions-and-roles) for adding custom permissions
- [Database Schema](/docs/next-supabase-turbo/development/database-schema) for extending your schema
- [Row Level Security](/docs/next-supabase-turbo/security/row-level-security) for RLS patterns
- [Database Tests](/docs/next-supabase-turbo/development/database-tests) for testing database functions