DEV Community

Ume
Ume

Posted on

How I Designed Supabase and Row Level Security (RLS)

Designing a Secure API with Cloudflare Workers × Supabase

In this article, I explain how I approached Supabase and RLS, and how I incorporated them into my API design, based on a personal project built with:

React Native → Cloudflare Workers (Hono) → Supabase

The API assumes a simple use case: user-specific notes.

Supabase is powerful but very flexible, which makes it difficult to decide how much responsibility to delegate to it.

This article exists to clarify:

  • where I chose to rely on Supabase,
  • where I deliberately did not,
  • and why I made those decisions.

The concepts discussed here are demonstrated in the following repository:

https://github.com/umemura-dev/hono-supabase-auth-security-demo


1. How I Positioned Supabase (Initial Design Principles)

Supabase provides many features out of the box: database, authentication, storage, and auto-generated APIs.

Without clear boundaries, it’s easy for responsibilities to blur and the system to become complex.

So I fixed the following principles at the very beginning.

Principle 1

Supabase is responsible for data integrity and safety.

Principle 2

Final access decisions must always be enforced by RLS.

Principle 3

Business logic, input validation, and error handling live in Workers (the API layer).

Principle 4

Client-side privileges are kept minimal, and Service Role keys are never exposed.

By defining these four rules, each layer’s responsibility became clear,

and I avoided pushing application logic into Supabase unnecessarily.


2. Supabase Components Used in This Project

Auth (Authentication)

The role of Supabase Auth in this design is simple:

to reliably identify the user (UID).

On the Workers side, the flow is:

  1. Extract the JWT from the Authorization header
  2. Verify its signature using Supabase’s JWT_SECRET
  3. Treat the sub claim (UID) as userId

This userId is stored in the request context and reused for database operations.


Database (PostgreSQL)

This demo uses a single table, demo_notes,

designed to store user-specific data.

Example columns:

  • id
  • user_id
  • title
  • body
  • created_at
  • updated_at
  • deleted_at

The deleted_at column enables soft deletes, avoiding physical row deletion.


RLS (Row Level Security)

RLS is a native PostgreSQL feature that determines:

“Is this user allowed to access this row?”

Supabase tightly integrates Auth and RLS,

allowing the JWT’s UID (sub) to be referenced as auth.uid().

Even if Workers validates userId,

without RLS there would still be a risk of data exposure if a malicious client accessed Supabase directly.

For that reason, RLS is always treated as the final defensive layer.


3. Example RLS Policies (demo_notes)

Below is a minimal RLS setup used in this project.

In real-world systems, you would typically extend this with read-only roles, admin roles, etc.

All policies use to authenticated, meaning:
only users authenticated via Supabase Auth are eligible.

This cleanly blocks unauthenticated or malicious access.


SELECT (Only fetch your own notes)

create policy "select_own_notes"
on demo_notes
for select
to authenticated
using (
  auth.uid() = user_id
);
Enter fullscreen mode Exit fullscreen mode

INSERT (Only create notes for yourself)

create policy "insert_own_notes"
on demo_notes
for insert
to authenticated
with check (
  auth.uid() = user_id
);
Enter fullscreen mode Exit fullscreen mode

UPDATE (Only update your own notes)

create policy "update_own_notes"
on demo_notes
for update
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
Enter fullscreen mode Exit fullscreen mode

Note:

DELETE is not exposed via the API.

Instead, notes are soft-deleted by updating deleted_at.


4. How I Designed the Workers (Hono API)

Responsibilities are clearly split as follows.

Workers Responsibilities

  • JWT verification (authentication)
  • Extracting userId
  • Input validation (Valibot)
  • Application-specific logic (e.g. soft delete)
  • Preparing data for Supabase
  • Logging, rate limiting, error handling

Supabase (RLS) Responsibilities

  • Determining row-level access permissions
  • Blocking unauthorized access even if the API layer is bypassed

No matter how careful the Workers layer is,

it can never be considered perfectly secure.

By enabling RLS, I ensure that even if a request reaches the database, other users’ data remains inaccessible.


5. Why I Use Supabase RLS (Compared to Plain PostgreSQL)

RLS is available in PostgreSQL everywhere,

but I chose Supabase for three main reasons.

Automatic Auth–RLS Integration

Supabase provides a built-in bridge between JWTs and auth.uid(),

dramatically reducing design and implementation complexity.


Safe Even with Direct Client Access

Even when using Supabase’s official PostgREST API or client SDKs,

RLS policies are always enforced.

Security does not depend on routing everything through Workers.

The client SDK also supports automatic token refresh,

which simplifies client-side implementation.

That said, designing a system that safely supports direct access requires:

  • careful RLS design,
  • query design,
  • cache strategies,
  • and well-defined permission boundaries.

Since this project focuses on API security design,

I intentionally centralized access through Workers.


Strong Security Even for Personal Projects

RLS policies are defined in simple SQL and can be customized with conditions.

Once you understand the syntax, it becomes relatively easy

to enforce strong guarantees—even in small personal projects.


6. Summary: What This Architecture Prioritizes

Although this is a small demo project, the design emphasizes:

  • Workers (API) handle application behavior
  • RLS (DB) provides the final security boundary
  • Supabase Auth establishes user identity

With this structure,

I believe the risk of catastrophic data leaks is significantly reduced.

Going forward, I plan to expand on this foundation by documenting:

  • request signing strategies,
  • and additional API-side security patterns.

Demo Implementation

Everything described in this article is implemented in the following repository:

  • Cloudflare Workers + Hono API
  • Supabase Auth (JWT) + PostgreSQL RLS
  • App Guard (signature-based client authentication)
  • CI passing (Biome / TypeScript / Tests)

GitHub:

https://github.com/umemura-dev/hono-supabase-auth-security-demo

Top comments (0)