prisma
prisma copied to clipboard
Define type of content of `Json` field
Problem
Right now if you have the following schema with Json field:
model User {
id Int @default(autoincrement()) @id
name String?
extendedProfile Json
}
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.
Suggested solution
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
}
Alternatives
Alternatively, we can somehow manage this in the typescript.
Have you looked into this?
Yes, but i don't understand how it can help in this problem. I may be wrong, but you cannot define the shape of the Json field using this tutorial..
Interesting idea, though all fields should be nullable since there is no guarantee for JSON format enforced by the database.
Interesting idea, though all fields should be nullable since there is no guarantee for JSON format enforced by the database.
Probably, if the database you are using does not have JSON support, then you simply cannot use the Json field type and this feature.
Probably, if the database you are using does not have JSON support, then you simply cannot use the Json field type and this feature.
I think @Sytten is talking about the fact in Json type say in postgres doesn't enforce any schema. It will not guarantee that all data there follows the shape you define. The proposal you have defined here is will all be enforced on application level.
Also, @MaximNd why not define a 1-1 relation here if you want a schema to be maintained, just interested to know. The main selling point of Json type personally for me is it allows me to throw data in it without worrying about it's shape.
Probably, if the database you are using does not have JSON support, then you simply cannot use the Json field type and this feature.
I think @Sytten is talking about the fact in Json type say in postgres doesn't enforce any schema. It will not guarantee that all data there follows the shape you define. The proposal you have defined here is will all be enforced on application level.
Also, @MaximNd why not define a 1-1 relation here if you want a schema to be maintained, just interested to know. The main selling point of Json type personally for me is it allows me to throw data in it without worrying about it's shape.
Yes you are right. In this example I can define a 1-1 relationship, or if I store an array in a Json field, I can define a 1-m relationship. But sometimes, when you have a simple data structure (for example, only a few fields) and you know that this json data will be relate only to this entry, then it is easier to define a Json field. The advantage of this is that you don't need to run an additional query or use JOINS to get the relational data. There might also be a rare case when some users that move their database from NoSql like Mongo to relational database. And there will be a lot of destructured data.
Being able to type your Json fields is a simple and understandable feature requests. Although there might be workarounds, this might very well be something that Prisma could offer in the future on its own - so having this feature request is valid.
I have tried the following workaround. It works fine, until I need a field other than number or string, e.g. Date
. Without date field the approach below works. I tried to use transformers for string to date conversion, but that contradicts Prisma.InputJsonObject
definition.
import { Type } from "class-transformer/decorators";
import { IsOptional, Length } from "class-validator";
export class Qualification implements Prisma.InputJsonObject {
@Length(1, 30)
name?: string;
@IsOptional()
age?:number;
@IsOptional()
@Type(() => Date)
birthday?: Date;
[index: string]: Prisma.JsonValue ;
}
Any suggestions?
@husayt What is this approach? I googled "InputJsonObject" and prisma and only came up with this post and https://github.com/prisma/docs/issues/669 which only has "InputJsonObject" in a list
A note on potential interaction with #2431 and #2444: As JSON may be used for static or dynamic content, this should be opt in - though ideally, you could still select and filter on specific keys even if you have not typed the JSON field. In the specific use case I'm thinking of, I would actually maintain a separate table that would define which rows contain which keys. Specifically, I have a puzzle with multiple solutions. A solution has a JSON field defining some details which depend on the configuration of the puzzle it is for.
I just ran into a need for this similar to OP's - I am using JSON as a way to store reliably structured data that is small, has several child fields, and is always 1:1 linked with the parent. Because of that, it feels wasteful to break it out into another table and require JOINs on every query.
I was expecting something to be available that was akin to the custom scalar
operator in the GraphQL SDL - where you can define a name for a field type (and, in Prisma's case, define what type the column should be given in the schema) and then you are responsible for defining the shape yourself in the client code. You could imagine it working something like:
generator client {
provider = "prisma-client-js"
scalarDefinitions {
RuleContents = "../../../prisma-scalars/RuleContents.ts"
}
}
scalar RuleContents @underlyingType(Json)
model Rule {
id String @id @default(uuid())
rejectRule RuleContents?
favoriteRule RuleContents?
}
Then the referenced Typescript file would export an interface with the same name as your scalar. In the simplest form, the client would just be applying a cast for data read from the DB and type checking on data written to or queried from it. As an optional future enhancement the scalar definition file might even be able to export a custom serialize and deserialize function that the Prisma client would use to transform data before writing to/reading from the database.
Finally, if you were generating a client where the scalar type wasn't provided, the client could fall back to the @underlyingType
and you'd get the same behavior we have now - e.g. if your underlying type was Json it would fallback to a basic any
in Typescript, if your underlying was Unsupported(Polygon) it would fall back to hiding the field, etc.
Adding some light to this, it's a common problem with GraphQL. If an input sets the shape of an object, this will fail type validation when interacting with Prisma. Here is an example where ApplicationFieldConfig[]
is coming from my GraphQL library.
This would be a huge lifesaver! Right now I have to cast each property to unknown and then my own type, while also removing the need of having our own handling for JSON types.
I don't want to start a bad trend here, but +1 from me too! I'd like to be able to customize/extend Json column types in TypeScript.
Thanks for all your work here πΊ!
Solved the issue with typechecking in Nest.js application by using class-transformer's classToPlain + plainToClass:
I don't want to start a bad trend here, but +1 from me too! I'd like to be able to customize/extend Json column types in TypeScript.
Thanks for all your work here πΊ!
Yes really mainly because MongoDB is now supported we needed a deep level type-safety, and will also be applicable to Postgres JSON data type
@glebbash solutions works as of now. But at the end you will also wanted a single source of truth, your schema.prisma file
I'm going to drop a suggestion here. A JSON definition written in Prisma would leave the team to need to maintain parity with Typescript in the schema file. A better solution might be to reference the type alone:
model Post {
id Int @id @defualt(autoincrement())
data Json @typescript(PostData)
}
The only difficulty here is I'm not sure how prisma would know where to find the PostData definition.
Another possible solution would be to provide types at the client level:
new PrismaClient<{ Post: { data: PostData } }>()
@fny
new PrismaClient<{ Post: { data: PostData } }>()
Would be a good solution here.
@fny @mmahalwy Great ideas :) I love it how it could be a simple fix, that will give an almost instant solution. Even though I think it will make a great solution, I think that the whole point is not creating TS types, and have those synced with the prisma schema right?
Yeah, while that would be a pretty easy fix (indeed, one could probably write that as an npm package that wrapped Prisma), it would not actually enforce the type to the same level that Prisma enforces other types from the schema, nor would it necessarily enforce the same type across projects that shared a single schema file for cross-compatibility.
We already accept that Prisma generates Typescript typing for us as part of client generation, so I would rather typing for these json fields were kept in the Prisma schema and generated into TS along with the rest of the models.
We could have a similar approach done by the graphql schema:
type Hotel {
title: String
rooms: Room[]
}
type Room {
name: String
size: String
}
model Something {
registration: Int
hotelData: Json<Hotel>
}
Keen
Is there any progress on this ticket? More and more applications are using json columns in their databases and so it would be a great feature to support this, especially because Prismaβs main advantage is type safety.
Up β we really need this feature π
+1
Is there a place where we can check the priority of this feature? I mean I β€οΈ Prisma (I really do!), but it seems quite an obvious feature, no?
+1 on this this is really needed tbh
Is there a place where we can check the priority of this feature? I mean I β€οΈ Prisma (I really do!), but it seems quite an obvious feature, no?
https://pris.ly/roadmap @binajmen
Is there a place where we can check the priority of this feature? I mean I β€οΈ Prisma (I really do!), but it seems quite an obvious feature, no?
https://pris.ly/roadmap @binajmen
Thank you @pantharshit00. Although typing "json" in the search field does not highlight any backlog entry related to this issue, only 1 archive https://www.notion.so/JSON-field-improvements-5acca22f9a474ab4a8f67e19d412cc25 seems json related, but not answering the feature discussed in this thread.
Is this open issue with 148 π and 30 π the only way to support this feature implementation?
Does anybody have a simple workaround for this? We are currently forced to do some TS generic wrapping everywhere we return something from Prisma, as the select
fields might change, and if containing the JSON-field, it needs to be converted, like:
Omit<T, "myJsonField"> & {myJsonField: MyJsonType}
where T
is some generic that needs to be filled with typeof stuffReturnedFromPrisma
...
which leads to code like:
const stuffReturnedFromPrisma = await prisma.MyModel.create(...);
const typedStuff = stuffReturnedFromPrisma as MyJsonTypeConvert<typeof stuffReturnedFromPrisma>;
or similar. And that's pretty verbose.
Is this open issue with 148 π and 30 π the only way to support this feature implementation?
Yes. Or posting additional use cases and information about how you want to use this.