question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Extend Prisma Client

See original GitHub issue

Problem

Right now we don’t provide guidance on how use the Prisma Client within a higher-level model or domain.

Use cases

1. Adding a computed field: The canonical example is getting a full name from a first and last name. We also have some use cases in: https://github.com/prisma/prisma/issues/3394 and https://github.com/prisma/prisma/issues/5998

2. Multi-step actions: Right now people put all their Prisma Client calls inside their GraphQL resolvers or RESTful controllers. This leads to “fat controllers” that are hard to test and lead to code duplication across controllers. The reason is because the Prisma client is often lower-level than your business domain and expressing your business logic requires multiple steps.

Some Examples:

  • team.signup: signing up for Slack requires you to create a user and a team at the same time
  • document.update: when you update a document, you also update its search index and fire off an email to subscribers

From a top-level view of your application, you’d consider these to be a single action (e.g. team.signup and document.update), but they break down into multiple steps. We should have a way to turn multiple steps into single actions without limiting the rich capabilities of the Prisma Client API. https://github.com/prisma/prisma/issues/5258 https://github.com/prisma/prisma/issues/5258

3. Custom Validation: Validate fields before they hit the database. Using the example above, for a team.signup(team_name, owner_email) action, you can validate that the owner’s email address is valid and the team name doesn’t have any special characters before you go to the database. Lots more use cases in: https://github.com/prisma/prisma/issues/3528

Existing Solutions

  • Middleware: Is one way to solve the multi-step actions triggering other actions, but you wouldn’t really want to trigger other Prisma Client actions within this middleware. It’s also global configuration for the entire instance of the client. Models can be more flexible.
  • Doing it in your resolver or controller: This works fine for simple applications. When you start sharing this logic, you need a way to abstract shared business logic into a model.

Prior Art

In Rails you have a difference between Active Record and the Active Record Query Interface. Prisma Client is an awesome Active Record Query Interface but Active Record is currently DIY. We should also have a story for some of the Active Record use cases described above.

Suggested Next Steps

  • Create an example script with the Prisma Client we have today demonstrating the use cases above. E.g. A model with computed fields, multi-step actions and custom validation.
  • Do we lose the rich querying capabilities of the Client? What can we do about that? Can we have both? If so, how could we have both?
  • How easy was this script to write with our existing generated type definitions? Anything we can do to improve this common workflow?
  • Document how to do currently this so we can point users to this documentation whenever they ask about it. Create new issues for larger future improvements.

What I’d like to avoid: Going too deep into implementing a solution before understanding how one would do this with today’s Prisma Client in a hacky way perhaps.

Related

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:76
  • Comments:32 (8 by maintainers)

github_iconTop GitHub Comments

12reactions
brianreevecommented, Oct 25, 2021

Seconded.

I’ve been evaluating Node ORMs supporting MySQL for a couple weeks. I was turned on to Prisma because it seems to provide fantastic tooling orbiting around a central schema definition such as generating and managing migrations.

What seems to be lacking is generating extendable model classes which encapsulate the data representation. Typically (my personal experience across various languages), you’d see ORM tooling generate base classes with accessors for each column. Instances of which represent rows in the database. Then, developers can extend those model classes with additional functionality.

The use cases are many as you’ve already pointed out, but here is a list of ones I commonly face:

  • Validation
  • Manipulate data on read or write: sanitization, normalization, encryption/decryption
  • Non-DB functionality - eg. read/write a user’s avatar in cloud storage
  • Do multiple things - eg. archive user - copy data to static storage, then delete from DB, then notify someone

Fat controllers don’t cut it because objects are often manipulated in multiple contexts: CLI, MVC controllers, REST API actions, etc. and you likely want to encapsulate coupled functionality right at the model level for DRY reasons.

I briefly looked into documentation and extending the client to achieve equivalent results, but there isn’t even any tangible code representing the “Active Record”. It seems that dynamically creates objects based on schema rather than providing a base class per table.

Can we at least get an estimate on some documentation which explains how to achieve this, and the pros/cons? Perhaps this would accomplish your first bullet:

Create an example script with the Prisma Client we have today demonstrating the use cases above. E.g. A model with computed fields, multi-step actions and custom validation.

I have limited knowledge of the inner workings, but can we hook into the client to tell it how to serialize/deserialize data per table? That way we could write our own classes and pass around instance of them as long as the Prisma client can understand how to handle them. We’d probably have to write some boilerplate (accessors mostly) for each, but that’s a reasonable expectation and can be solved with tooling.


Update:

https://github.com/prisma/ent/issues/1

This is exactly it but prisma/ent looks like a rough POC and is 3.5 years stale. However, it also seeks to solve another problem… extending what that repo terms “repositories”.

Is this type of thing on the roadmap?

10reactions
matthewmuellercommented, Dec 1, 2021

Hey folks, so I compiled a list of patterns that you can use to extend the Prisma Client today. This is a first draft. Please let me know how helpful you find this and if there’s anything I should add, change, clarify, etc.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Custom models - Prisma
This page explains how to wrap Prisma Client in custom models. ... We suggest either wrapping a model in a class or extending...
Read more >
Extend your Platformatic API with Prisma Client
Learn how you can extend your auto-generated REST and GraphQL API by Platformatic using Prisma Client. Tagged with node, prisma, ...
Read more >
Prisma 4.7 Introduces Client Extensions
Extending the Client. Client extensions currently allow you to modify 4 aspects of the Prisma client. The top-level client, your models, queries ...
Read more >
Prisma/Typescript: how to extend client? - Stack Overflow
import { PrismaClient } from '@prisma/client'; export type NotStartsWith< S, Prefix extends string, > = S extends `${Prefix}${infer _X}` ...
Read more >
Learn Extend, Prisma and Solidity | egghead.io
Build a Graphql Serverless Database for Your Client Apps Using Prisma ... Perform basic database queries (CRUD) with Prisma Client. Xiaoru Li.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found