apps/docs/content/guides/database/postgres/column-level-security.mdx
PostgreSQL's Row Level Security (RLS) gives you granular control over who can access rows of data. However, it doesn't give you control over which columns they can access within rows. Sometimes you want to restrict access to specific columns in your database. Column Level Privileges allows you to do just that.
<Admonition type="caution">This is an advanced feature. We do not recommend using column-level privileges for most users. Instead, we recommend using RLS policies in combination with a dedicated table for handling user roles.
</Admonition> <Admonition type="caution">Restricted roles cannot use the wildcard operator (*) on the affected table. Instead of using SELECT * FROM <restricted_table>; or its API equivalent, you must specify the column names explicitly.
Policies in Row Level Security (RLS) are used to restrict access to rows in a table. Think of them like adding a WHERE clause to every query.
For example, let's assume you have a posts table with the following columns:
iduser_idtitlecontentcreated_atupdated_atYou can restrict updates to just the user who created it using RLS, with the following policy:
create policy "Allow update for owners" on posts for
update
using ((select auth.uid()) = user_id);
However, this gives the post owner full access to update the row, including all of the columns.
To restrict access to columns, you can use Privileges.
There are two types of privileges in Postgres:
You can have both types of privileges on the same table. If you have both, and you revoke the column-level privilege, the table-level privilege will still be in effect.
By default, our table will have a table-level UPDATE privilege, which means that the authenticated role can update all the columns in the table.
revoke
update
on table public.posts
from
authenticated;
grant
update
(title, content) on table public.posts to authenticated;
In the above example, we are revoking the table-level UPDATE privilege from the authenticated role and granting a column-level UPDATE privilege on just the title and content columns.
If we want to restrict access to updating the title column:
revoke
update
(title) on table public.posts
from
authenticated;
This time, we are revoking the column-level UPDATE privilege of the title column from the authenticated role. We didn't need to revoke the table-level UPDATE privilege because it's already revoked.
Column-level privileges are a powerful tool, but they're also quite advanced and in many cases, not the best fit for common access control needs. For that reason, we've intentionally moved the UI for this feature under the Feature Preview section in the dashboard.
</Admonition>You can view and edit the privileges in the Supabase Studio.
While you can manage privileges directly from the Dashboard, as your project grows you may want to manage them in your migrations. Read about database migrations in the Local Development guide.
<StepHikeCompact><StepHikeCompact.Step step={1}> <StepHikeCompact.Details title="Create a migration file">
To get started, generate a [new migration](/docs/reference/cli/supabase-migration-new) to store the SQL needed to create your table along with row and column-level privileges.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
supabase migration new create_posts_table
</StepHikeCompact.Code>
</StepHikeCompact.Step> </StepHikeCompact>
<StepHikeCompact><StepHikeCompact.Step step={2}> <StepHikeCompact.Details title="Add the SQL to your migration file"> This creates a new migration: supabase/migrations/<timestamp> _create_posts_table.sql.
To that file, add the SQL to create this `posts` table with row and column-level privileges.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```sql
create table
posts (
id bigint primary key generated always as identity,
user_id text,
title text,
content text,
created_at timestamptz default now()
updated_at timestamptz default now()
);
-- Add row-level security
create policy "Allow update for owners" on posts for
update
using ((select auth.uid()) = user_id);
-- Add column-level security
revoke
update
(title) on table public.posts
from
authenticated;
```
</StepHikeCompact.Code>
</StepHikeCompact.Step> </StepHikeCompact>
select * will fail.