Is there a way to enforce that all queries include a where clause on tenantId
?
#1539
Replies: 7 comments 13 replies
-
Would wraping the query in a function and using a branded type be a good idea? Something like this: type AdminTenantId = Brand<string, "AdminTenantId">
const adminTenantId: AdminTenantId = requireAdminTenantId(request)
/**
* @param {any} options - the query options.
* @param {AdminTenantId} tenantId.
*/
const data = await getAdminData(options, tenantId) The runtime check would occur in |
Beta Was this translation helpful? Give feedback.
-
Scratch that, I've found a solution that I am much, much happier with. Instead of exporting tables directly for use by the application, I instead export subqueries that already include the constraint on tenant id (which are created on demand on each request). Imports of the tables themselves are restricted, so all I can import in the rest of the app are functions like this: export const person = (tenant: { id: string }) =>
db
.select()
.from(s.person)
.where(eq(s.person.tenantId, tenant.id))
.as("person"); which are then used like this: const people = await db
.select()
.from(person(tenant))
.where(like(person(tenant).name, "%Cronenberg%"))
); which has the nice effect of meaning every table in the query will have the constraint applied, not just the root table. will also be able to use this for soft deletes which will be very helpful too. the only limitation i've encountered so far: i can't figure out how it could be possible to make this work for relational queries, which i'd really like to use. is there any way to make this possible? i initially tried creating the schema like this: export const db = drizzle(sql, { schema: { person: person(tenant) } }); but of course this doesn't work because the would be amazing if there was a way to achieve this. |
Beta Was this translation helpful? Give feedback.
-
I was having the same question, coming from another ORM myself. Unfortunately, I couldn't find a solution that suited my needs so I decided to create one from scratch. It utilizes proxies to intercept Drizzle functions. You can check the gist here. |
Beta Was this translation helpful? Give feedback.
-
I haven't gone deep into this yet, but maybe adapting the drizzle eslint plugin could be an alternate approach: Like taking the enforce-update-with-where and make it check that the where clause has a "tenantID" field. Might be fiddly to get right depending on how your tables are structured though. |
Beta Was this translation helpful? Give feedback.
-
Could someone achieve it simply ? Either using eslint plugin with specific enforcing or any other solution? |
Beta Was this translation helpful? Give feedback.
-
I'm only scoping out this library, but this is a feature I find myself needing repeatedly in multi-tenant apps that share the same database. I would love to see this implemented. I have seen it done a few ways: before query lifecycle hooks on the models that allow you to add filters to the query. Global middleware that intercepts and allows modification of all queries. I have no strong preference on the approach, but this is a pain point for multi-tenant apps. |
Beta Was this translation helpful? Give feedback.
-
If you want to have the strongest isolation between tenants but still use the same database, you essentially have 2 choices:
Here's how I do it (option 2):
This way, it's impossible for tenants to access one another's data if they always use the views. The real trick is getting Note: I believe this is how Supabase works, minus all the RLS and security stuff they have in place. |
Beta Was this translation helpful? Give feedback.
-
I'm writing a single-database, multi-tenant app and would like to make sure every query contains a filter on
tenantId
so I don't accidentally show data from one tenant to another. Doesn't need to do anything fancy like rewrite the query, just check if it's there and throw an exception if it isn't. Ideally would be able to check that the correcttenantId
is specified so would be great if the check could be changed on a request per request basis. Have commented on another discussion about hooks which feel like they could be a good way to achieve this, but thought I'd ask it as a separate question in case there already is a way to do this?Beta Was this translation helpful? Give feedback.
All reactions