zenstack icon indicating copy to clipboard operation
zenstack copied to clipboard

[Feature Request] Model-Level CRUD Hooks

Open andrewpeters9 opened this issue 1 year ago • 4 comments

Is your feature request related to a problem? Please describe.

  • Prisma's current API for hooks (esp. at model level) is lacking.
    • .$extends requires explicit method wrapping, and doesn't support method-level extending.
    • Computed fields (which can cover some use-cases of READ hooks) are methods, and therefore unserializable - meaning, they're unable to be passed to the frontend unless explicitly called in the server
  • Prisma Pulse is the current best solution, but is a separate third-party service (which currently only supports Postgres) and therefore may not be a practical solution for most.
  • Zenstack's philosophy seems to jibe with locking down actions/logic to the model level, and therefore, thinking about implementing model-level CRUD hooks in the future may be appropriate

Example Usecases:

  • Create: Famous Instagram-DB use-case of implementing a counter on a model-level field for # of likes as the db history of individual like objects is too big to cache. I.e., creation of Like document would fire a hook that updates their Post document.
  • Read: Some high-security apps need to keep read logs for everything an employee has accessed.
  • Update: After updating User model with a membership level, you may want to send the user an email thanking them for purchasing xyz subscription
  • Delete: cleaning up other model rows that may not be able to directly link to the deleted record for whatever reason

Describe the solution you'd like

  • Definable-hooks applied at the enhance call, i.e. allowing the user to execute JS based on
  • Hooks will be fired even if I update a model via a call from a model above/below:
user.update({
   data: {
      subscription: {
          create: {
              // etc ... 
          }
      }
   }
})
  • Post-MVP: Hooks would be able to access the prev/post document (or maybe you could have preX, postX hooks? - Post-Post-MVP may allow for pre hooks to cancel an action (pie-in-the-sky, I know ...))
  • Post-MVP: provider the hook's function params access to the user that made the CRUD request. Useful for implementing read-logs etc.

Potential Challenges:

  • AFAIK, there isn't an inbuilt API that wraps the Prisma core methods
  • Determining when a model is CRUD'ed via a parent/children call may be tricky too

Comment from @ymc9

This is some thought background about access conrol. As for CRUD hooks, I fully agree with you that implementing with Prisma client extensions can be quite challenging given the flexibility of nesting - it's very easy to have a leaky implementation. I think it's a good idea that we "reimplement" some kind of hook at the TS-API land, like allowing you to provide simple callbacks when calling enhance (model + operation + action), and the API internally deals with traversing nesting, and probably also guarantees a transaction boundary.

andrewpeters9 avatar Mar 11 '24 23:03 andrewpeters9

For an idea of how this would work at a high level, have a look at keystone-js, which is also built on top of Prisma.

They're docs explain the implementation pretty well: https://keystonejs.com/docs/guides/hooks and the hooks api: https://keystonejs.com/docs/config/hooks

genu avatar Jul 11 '24 18:07 genu