PostgreSQL:Grant
lastmod 2026-02-26created 2026-02-253 edits
Relationship between GRANT and RLS
In PostgreSQL, table access is controlled in two stages.
- Stage 1: GRANT (table-level privileges)
- "Can this role access this table?"
- Without a GRANT, the role cannot access the table at all. It is like being stopped at the door.
- Stage 2: RLS (row-level privileges)
- "Among the roles allowed access, which rows can they view/modify?"
- RLS is applied after passing GRANT. It decides which of the people who got through the door can enter which rooms.
Supabase role structure
- anon - users who are not logged in (access with the API key only)
- authenticated - logged-in users (auth.uid() exists)
- service_role - server-side administrator — bypasses RLS
If the table has no GRANT:
If you add a GRANT:
Meaning of each privilege
GRANT SELECT, -- 조회 (SELECT 쿼리)
INSERT, -- 삽입 (다른 관리자 추가)
UPDATE, -- 수정
DELETE -- 삭제
ON public.my_table -- 테이블 이름
TO authenticated; -- 로그인한 사용자 역할에게
Summary:
- RLS without GRANT → no one can access
- GRANT only, no RLS → every authenticated user can access all rows
- GRANT + RLS → only authenticated users who satisfy the policy can access
How to check a table's grants
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_schema = 'public'
AND table_name = 'admins';
When there is no GRANT:
When there is a GRANT:
grantee | privilege_type
----------------+---------------
authenticated | SELECT
authenticated | INSERT
authenticated | UPDATE
authenticated | DELETE
Supabase's automatic GRANT
By default, Supabase sets the following default privileges on the public schema.
Because of this setting, creating a new table in public automatically applies GRANTs to anon and authenticated.
From a security standpoint, if there is no reason for non-logged-in users to access the table, it is better to remove the anon role's privileges.
PUBLIC grantee
A virtual group meaning every role in PostgreSQL.
- GRANT to PUBLIC → every current and future role gets that privilege
- REVOKE from PUBLIC → removes that default privilege
When you create a function, PostgreSQL automatically applies "GRANT EXECUTE TO PUBLIC". That is why anyone, including anon, could run it unless it was explicitly REVOKEd.