Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One Prisma client per call? #1

Open
WesleyYue opened this issue Mar 8, 2023 · 2 comments
Open

One Prisma client per call? #1

WesleyYue opened this issue Mar 8, 2023 · 2 comments

Comments

@WesleyYue
Copy link

Hi, thanks for writing up this example. Do you have any guidance on how to manage the number of Prisma clients? Typically, you'd have one Prisma client for the application. Given that each Prisma client now has their own unique extension with the appropriate claims, what is the recommended pattern for managing Prisma client instances?

@dthyresson
Copy link
Owner

dthyresson commented Mar 8, 2023

Hi @WesleyYue thanks for your question.

Do you have any guidance on how to manage the number of Prisma clients? Typically, you'd have one Prisma client for the application.

I think I would still have one client per application, or maybe two (not including what Prisma would use to run its migrations using the directUrl connection string that I would keep superuser/admin rights on).

1 - A client that was used by the application to execute queries based on behalf of a user. This would be the client that useSupabaseRowLevelSecurity extends.

2 - I may still have some admin client that bypasses RLS for its role (this would not use useSupabaseRowLevelSecurity and would specify a datasource url that grants more access and permissions).

Given that each Prisma client now has their own unique extension with the appropriate claims, what is the recommended pattern for managing Prisma client instances?

I don't think that would be the case that "each Prisma client now has their own unique extension with the appropriate claims" because there is a claimsFn that would dynamically set the claims per request.

What I mean is, seen in this example:

const prisma = new PrismaClient({
  datasources: { db: { url: process.env.RLS_DATABASE_URL } },
}).$extends(
  useSupabaseRowLevelSecurity({  
  /**
   * Return the decoded current user from the context
   */
  claimsFn: () => context.currentUser,
  /**
   * Throw a RedwoodJS ForbiddenError if the policy is violated
   */
  policyError: new ForbiddenError('Violates RLS.'),
  })
)

The claimsFn will get the currentUser here from context. In RedwoodJS, this object would have the necessary JWT claims for Supabase to use to authenticate and apply RLS policies.

So, this "app res-enforced client" can be used over an over for any user.

Perhaps I am not understanding your question, but beyond an app or admin Prisma client, what other clients do you envision needed in your application?

For Supabase, it is common to use their auth functions to CHECK that the policy permissions are valid:

CREATE POLICY "Authenticated users can modify Pedals" ON "public"."Pedal"
AS PERMISSIVE FOR UPDATE
TO rls_user
USING (auth.jwt() ->> 'role' = 'authenticated')
WITH CHECK (auth.jwt() ->> 'role' = 'authenticated');

But, I guess if your policy uses "some other" check then the default claimsSetting of this client extension would not work.

It's geared to sending JWT claims so that auth.jwt() checks can work given that Supabase check claimsSetting: 'request.jwt.claims' and that's what's set.

I imagine you could override the default claimsSetting .. and then yes, you would need multiple rls-enforcing clients per claimsSetting for the type of queries you make, but for Supabase this is essentially user to always be that claimsSetting since that's how its own SDK works and what it expects.

Hope that helps!

@WesleyYue
Copy link
Author

Thanks, I think was misunderstanding how Prisma extensions worked, and I was not aware that you can extend the same base client instance without concerns for conflicts as the returned client instances from .$extends() are isolated from one another.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants