advanced · 25 minute study
An RPC is an exposed function, not a permission shortcut
A database function can put related work into one boundary. It can also accidentally expose privileged behavior if the caller and execution context are not understood.
Useful preparation · 2 prerequisites
Use this as a map, not a lock. If these ideas are familiar, continue.
- Check the row before and after a change — A user may be allowed to see an existing row yet still be forbidden to create its proposed replacement. Row reassignment is the classic test of this boundary.
- Reason about two writers, not one happy path — A single-user test can pass while two users sell the same last item. Concurrency changes what it means for a read to justify a later write.
First, build a mental model
Calling an RPC is entering a function through a public-facing door. EXECUTE decides who may enter; the execution context and downstream controls decide what the function may do.
- SECURITY INVOKER uses the caller's privileges. SECURITY DEFINER uses the function owner's privileges and therefore requires deliberate review of authority and exposure.
- Grant EXECUTE only to intended roles. PostgreSQL grants execute to PUBLIC for new functions by default unless default privileges are changed; make the intended exposure explicit.
- A privileged function needs its own trusted actor, resource scope and input checks. Caller-supplied company IDs, hidden routes and 'the UI already checked' are not sufficient.
- Constrain the search path and qualify object names in sensitive functions. Keep transaction boundaries, retry identity and direct invocation tests part of the design.
-- PostgreSQL / Supabase, illustrative notes schema.
CREATE FUNCTION public.rename_note(p_id uuid, p_title text)
RETURNS void LANGUAGE sql SECURITY INVOKER
SET search_path = '' AS $$
UPDATE public.notes SET title = p_title WHERE id = p_id;
$$;
REVOKE ALL ON FUNCTION public.rename_note(uuid, text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rename_note(uuid, text)
TO authenticated;
-- Caller still needs table privileges and applicable RLS.- Entry
The role needs EXECUTE on the particular function signature.
- Context
This invoker function runs with caller privileges; entering it does not manufacture table access.
- Data boundary
The UPDATE remains subject to its applicable privileges and row policies. The void sketch does not itself report whether a row changed.
Production thinking
Take this beyond the example.
Prefer the smallest function with a clear contract. Test the normal exposed invocation under real actor roles, not only an owner SQL session. Never publish a reference project's private function bodies or security manifests.
Your evidence
unseen is a local learning signal, not a credential or proof of production competence.
Explain the mental model, then explicitly acknowledge it. Opening a page is not evidence.
Supported retries can complete this lesson. Independent evidence and delayed review determine the skill stage.
Next lesson · Turn access claims into falsifiable tests →