First of all, this is not a post trying to blame Prisma and promote some other alternatives. Instead, I bet Prisma would be the best Typescript ORM dominant in the full-stack world. That’s why I switched to Prisma from TypeORM, which I have used for many years.
All the missing features I will discuss are from the top(most comments) issues of the Prisma GitHub repository. Therefore if you once run into any of them, I suggest you participate in the discussion thread of that issue which you can benefit from:
- Knowing some workaround/hack for this issue.
- Understanding the whole picture of the issue, especially for things you haven’t thought about.
- Seeing the pros and cons of different proposals and voting for the one you like best or even giving your own proposal.
Most importantly, let the community hear your voice and hope it will improve Prisma.
Custom Attribute
- Issue
Allow custom field attributes #3102
timsuchanek posted onLet's say I want to make sure, that an email is always valid before it gets created. It would be awesome, if I could add an
@email
directive like so:While it makes sense, that
@email
is not allowed right now, if we would allow it, users could now add an email validation middleware. Allowing top-level@email
probably doesn't make sense though to keep validation rather strict so that we can also help with typos.However, the same as in HTTP Headers, which can start with
X-
or HTML attributes, which can start withdata-
, we could add a namespace for custom directives, for example@custom.X
.That would allow a whole world of extensions for Prisma, which can be dealt with through middlewares.
And of course, this is just a wild idea, it could also just be trash and maybe we shouldn't do it. So let's have a discussion if this even makes sense :)
If I get to pick up only one to be implemented, it will be Custom Attribute, without a doubt. Not only because there are several issues that could be resolved by this below:
- Support SQL Check constraints #3388
- Add exclude in the query arguments #5042
- Support adding prefix to an @id field #3391
But also, this is like opening the door of the extension to the whole community. As Prisma advocates that we use the schema as the single source of truth for the model of our application, I think the prerequisite is that it has a good extension mechanism. Otherwise, Prisma would have to cover all the different cases by itself to achieve that universally.
Although Prisma Client Extensions feature has come out in version 4.7.0, which allows extending models programmatically in the client, it doesn’t support the advocating of the single source of the truth.
Since the issue has been open for two years since 2020, and there is not any update from the Prisma team yet, not even in the Prisma Roadmap, I’m afraid we won’t see it could come in a short time. I could understand that it would complicate the whole framework and add a burden for future updates. But isn’t that something you have to bear if you aim to build a prosperous ecosystem? Moreover, I think that’s really the power of the open-source community we could rely on. 😊
- Workaround
You can use make use of the comments to put whatever you need there, which could be accessed in the AST of the schema file. You need to implement your own generator to process it. It would be like what prisma-nestjs-graphql does :
model User {
id String @id @default(cuid())
/// @HideField()
password String
/// @HideField({ output: true, input: true })
secret String
/// @HideField({ match: '@(User|Comment)Create*Input' })
createdAt DateTime @default(now())
}
Soft Delete
- Issue
Soft deletes (e.g. deleted_at) #3398
matthewmueller posted onIt could be nice to add this kind of feature to core, so you get filtered views without cluttering up your application queries
Open questions:
- Would it be slow to add this filter to all queries?
- Can we do it only when we need it?
- Would this be better handled in Photon?
There was only one time I initiative upgrading the TypeORM version because of the official support of Soft Delete. See the below benefits I got from it for our SAAS product:
- Restore the data accidentally deleted by the customer
- Analyze customer behavior
- History tracking and audit
Therefore Soft Delete is almost mandatory for me. Lots of people might have a different opinion considering the challenge of the “proper” implementation. That’s why I think it’s up to the ORM to implement it so that we can use it just as simple as what we do for the normal delete. See what TypeORM provided:
@Entity()
export class User {
@DeleteDateColumn()
deletedDate: Date;
}
await myDataSource.createQueryBuilder("users").softDelete().where("id = :id", { id: 1 }).execute();
- Workaround
I’m not sure whether it’s even a workaround because it’s the official document of Prisma to specify how to implement the soft delete using middleware. Just take a glance at how long the article is and how many lines of code it contains, and you will see why I treat it as a workaround, and also, I guess that’s the reason why this issue has not been closed yet.
BTW, I feel using the new feature Prisma Client Extensions aforementioned to implement it might be more straightforward and simpler. Care to try it? 😉
Type of Json Field
- Issue
Define type of content of Json field #3219
MaximNd posted onRight now if you have the following schema with Json field:
You'll end up with a problem that you don't have strict type for
extendedProfile
field in.ts
.const user = prismaService.user.findOne(...); user.extendedProfile // we don't know the type of extendedProfile
The one way to fix it, is specify some interface in your code and use it like this:
interface UserProfile { field1: string; field2: number; } const user = prismaService.user.findOne(...); (user.extendedProfile as UserProfile).field1; // now we have autocompletion
But it's not really comfortable to use it like that each time.
Also we can create some class and create instance of it like that:
interface UserProfile { field1: string; field2: number; } class User { id: string; name?: string; extendedProfile: UserProfile; constructor(user: PrismaUser /* user object returned by prisma */) { // ... initialize } } const user = new User(prismaService.user.findOne(...));
But this solution creates some overhead due to the creation of an additional object.
Maybe we can specify type in
schema.prisma
file like that?json ExtendedUserProfileJson { field1 String field2 Int } model User { id Int @default(autoincrement()) @id name String? extendedProfile ExtendedUserProfileJson }
Alternatively, we can somehow manage this in the typescript.
Since JSON data type has been the first citizen of MySQL, we have used it a lot. It’s kind of like adopting the benefit of NoSQL in the SQL world.
In TypeORM, it’s really quite easy to use as it’s really very convenient to use. You can even use inline type definition to get type safety like the below:
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column("simple-json")
profile: { name: string; nickname: string };
}
In Prisma, of course, it’s not as easy as its schema first. But the suggested solution in the issue might be a way to go:
json ExtendedUserProfileJson {
field1 String
field2 Int
}
model User {
id Int @default(autoincrement()) @id
name String?
extendedProfile ExtendedUserProfileJson
}
- Workaround
You would need manually cast it in your code like below:
interface UserProfile {
field1: string;
field2: number;
}
const user = prismaService.user.findOne(...);
(user.extendedProfile as UserProfile).field1;
And the worse thing is that you don’t get type validation when writing.
Multiple Connections / Databases / Datasources
-
Issue
Multiple Connections / Databases / Datasources #2443
FredericLatour posted onAn application may need to access different connections/databases. One use case could be having the exact same set of tables into different databases (multi-tenant approach). Another use case could be having to access punctually some specific data in a separate database/server.
In any cases "a single connection" for the entire application is a really strong limitation. Any SQL driver or ORM is capable of connecting to multiple databases either by direct support (TypeOrm) or by creating 2 instances. Unless I missed something this is not possible with Prisma client.
I believe that it can be easily achieved with the following approach that does not change much both on the schema and client side of things.
datasource proj01 { provider = "postgresql" url = env("DATABASE_URL_PROJ01") models = [Post, User] } datasource proj02 { provider = "postgresql" url = env("DATABASE_URL_PROJ02") models = [Post, User] } datasource common { provider = "postgresql" url = env("DATABASE_URL_COMMON") models = [Config] } generator client { provider = "prisma-client-js" } model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) author User? @relation(fields: [authorId], references: [id]) authorId Int? } model User { id Int @id @default(autoincrement()) email String @unique name String? posts Post[] } model Config { id Int @id @default(autoincrement()) field string }
You would then need to make it possible to use one connection/db or another:
import { PrismaClient } from '@prisma/client' const proj01= new PrismaClient('proj01') const proj02= new PrismaClient('proj02') const proj01Users = await proj01.user.findMany() const proj02Users = await proj02.user.findMany()
Please note that it has nothing to do with having a dynamic url for being able to point to a different database when in development and/or staging. This feature is also needed but it's a different matter. Note also that this is not exclusively to solve "multi-tenant" scenarios. I may need to access a database on another server whatever the reason.
Thinking a bit more about it, this feature also provides a solution to the problem of having a different database (sqlite vs other database in prod for instance) depending on the environment (prod, staging, dev).
Adding on my previous example, you could have :
datasource proj01 { provider = "postgresql" url = env("DATABASE_URL_PROJ01") models = [Post, User] } datasource proj01-dev { provider = "sqlitel" url = env("DATABASE_URL_PROJ01") models = [Post, User] }
And then depending on the environment:
import { PrismaClient } from '@prisma/client' const proj01 = ( process.env.NODE_ENV === 'prod') ? new PrismaClient('proj01') : new PrismaClient('proj01-dev')
If somehow you have to access some specific data punctually in a separate database/server, this is like a MUST for you. Workaround
You need to create multiple data models like below:
In the schema
-
prisma/schema1.prisma
datasource db { provider = "postgres" url = env("DB1_URL") } generator client { provider = "prisma-client-js" output = "./generated/client1" } model Model1 { id Int @id @default(autoincrement()) model1Name String }
-
prisma/schema2.prisma
datasource db { provider = "postgres" url = env("DB2_URL") } generator client { provider = "prisma-client-js" output = "./generated/client2" } model Model2 { id Int @id @default(autoincrement()) model2Name String }
In the code
import { PrismaClient as PrismaClient1 } from "../prisma/generated/client1";
import { PrismaClient as PrismaClient2 } from "../prisma/generated/client2";
const client1 = new PrismaClient1();
const client2 = new PrismaClient2();
In the CLI
prisma generate --schema prisma/schema1.prisma
prisma generate --schema prisma/schema2.prisma
BTW, if you are using PostgresSQL and would like to query across multiple schemas, you are lucky that they have just released a preview feature for it. Check it out below:
And leave your feedback about it in the issue:
Preview feature feedback: Prisma multi schema support (`multiSchema`) #15077
Here are details on how the preview feature for multi schema support currently works in Prisma: https://github.com/prisma/prisma/issues/1122#issuecomment-1231773471
Please share your feedback about the multiSchema
functionality released in v4.3.0 in this issue.
- If you encounter a bug, please open a bug report in this repo.
- If the feature is working well for you, please share this in a comment below or leave a
👍 on this issue.
If you have any questions, don't hesitate to ask them in the #prisma-client
channel in the Prisma Slack.
Real Time API
-
Issue
Subscriptions/real-time API support #298
schickling posted onUnfortunately at the current point of time Prisma 2 doesn't yet support subscriptions (real-time API features). Prisma 1 followed a different architecture which provided support for subscriptions. We might be planning to bring back real-time/subscriptions functionality in the future based on a standalone "events engine" (which will use CDC under the hood).
This issue tracks the progress for this feature. Please feel free to share your specific requirements and wishes for this feature.
🙏 Workaround
I guess you have to implement it by yourself or find another solution for this.
Introducing ZenStack:a toolkit that supercharges Prisma ORM. We have managed to resolve the first two issues. Check out the posts I wrote for it: