Skip to content

Commit 2a09d3e

Browse files
authored
blog: update supabase rls alternative (#341)
1 parent 896e843 commit 2a09d3e

File tree

1 file changed

+23
-22
lines changed

1 file changed

+23
-22
lines changed

blog/supabase-alternative/index.md

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,26 @@ Everything looks perfect, especially if you are familiar with SQL. So why do I n
186186
![perfect-meme](https://github.com/user-attachments/assets/08ac420c-4f2b-4147-b4b5-a6c766ea61a3)
187187
## The Problems of Supabse RLS
188188

189-
### 1. **Separation from Application Logic**
189+
### 1. Separation from Application Logic
190190

191191
As a modern developer, you know the sense of control when you can launch with a one-click. The prerequisite is to keep everything within the database. It's not just about convenience; it's about maintaining a single source of truth, ensuring consistency, and streamlining your workflow.
192192

193193
However, for RLS, you have to define authorization directly in the database, not in your source code. Of course, you could store it as an SQL file in your codebase, but you need to rely on SQL migration to ensure consistency. I think it’s the same reason why you seldom see people using stored procedures of databases nowadays despite all the benefits they offer.
194194

195-
What makes the consistency even worse is that, according to the official documentation of Supabase, you have to duplicate the policy filter in your application code for performance reasons:
195+
What makes the consistency even worse is that you have to duplicate the policy filters in the application code. For example, if you are using the Supabase JS SDK, you have to use the two queries below to get the result:
196+
```tsx
197+
// First, get the user's spaceIds
198+
const { data: userSpaces } = await supabase.from('SpaceUser').select('spaceId').eq('userId', userId);
199+
200+
// Extract spaceIds from the result
201+
const userSpaceIds = userSpaces.map((space) => space.spaceId);
202+
203+
// Now, query the List table
204+
const { data, error } = await supabase
205+
.from('List')
206+
.select('*')
207+
```
208+
Why? Because otherwise, you might experience 20x slower query performance, according to the official benchmark of Supabase. 😲
196209

197210
[Add filters to every query | Supabase Docs](https://supabase.com/docs/guides/database/postgres/row-level-security#add-filters-to-every-query)
198211

@@ -318,38 +331,26 @@ What about the problems of RLS? Let’s go through them one by one.
318331

319332
The access policies are now defined alongside the database models. The schema becomes the single source of truth of your backend, enabling an easier understanding of the system as a whole.
320333

321-
Moreover, whether calling from the frontend or backend, you don’t need to use any filter regarding the authorization rules, which will be injected into queries automatically by the ZenStack runtime.
334+
Moreover, whether calling from the frontend or backend, you don’t need to use any filter regarding the authorization rules, which will be injected into queries automatically by the ZenStack runtime. The application code you need to write is clean and clear:
322335

323336
```tsx
324-
// frontend query
325-
const { data: lists } = useFindManyList(
326-
{
327-
where: {
328-
space: {
329-
slug: router.query.slug as string,
330-
},
331-
},
332-
}
333-
);
337+
// frontend query:
338+
// ZenStack generated hooks only returns the data user is allowed to read
339+
const { data: lists } = useFindManyList();
334340
...
335341
336-
// server props
342+
// server props
337343
export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res, params }) => {
338344
const db = await getEnhancedPrisma({ req, res });
339-
340-
const lists = await db.list.findMany({
341-
where: {
342-
space: { slug: params?.slug as string },
343-
}
344-
});
345+
// ZenStack enhanced Prisma client only returns the data user is allowed to read
346+
const lists = await db.list.findMany();
345347
return {
346348
props: { lists },
347349
};
348350
};
349351
```
350352

351-
> The `where` clause above is to get the data from current space because one user could join multiple `Space`. It’s the application behavior instead of Authorization
352-
>
353+
> Remember the complex query you need to write for the RLS case mentioned above?
353354

354355
### 2. Good DX
355356

0 commit comments

Comments
 (0)