I'm encountering some interesting behaviour when using Prisma ORM. It is related to Prisma's generated types, and I've been skimming the docs trying to find out more, but there doesn't seem to be much info about generated types in there (please correct me if I'm mistaken). Here's the behaviour:
Say I have a model with two 1-1 relations (Profile in the example below):
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int #id #default(autoincrement())
name String
profile Profile?
}
model Profile {
id Int #id #default(autoincrement())
name String
userId Int?
user User? #relation(fields: [userId], references: [id])
photoId Int?
photo Photo? #relation(fields: [photoId], references: [id])
}
model Photo {
id Int #id #default(autoincrement())
url String
profile Profile?
}
The following code works when creating a new profile:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
user: { connect: { id: user.id } },
photo: { create: { url: "http://example.com/img" } },
},
});
... but this fails with an error:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
userId: user.id,
photo: { create: { url: "http://example.com/img" } },
},
});
The error is:
Unknown arg userId in data.userId for type ProfileCreateInput. Did you mean user? Available args:
type ProfileCreateInput {
name: String
user?: UserCreateNestedOneWithoutProfileInput
photo?: PhotoCreateNestedOneWithoutProfileInput
}
Why is the second create-profile code invalid?
Prisma essentially generates two type definitions for a create query. This is implemented with a XOR type, which ensures that only one definition out of two is fully specified and passed to the query:
export type ProfileCreateArgs = {
/* ... */
data: XOR<ProfileCreateInput, ProfileUncheckedCreateInput>;
}
The definitions are called checked and unchecked, the former using nested fields and the latter using raw ids:
export type ProfileCreateInput = {
id?: number;
/* ... */
user?: UserCreateNestedOneWithoutProfileInput;
photo?: PhotoCreateNestedOneWithoutProfileInput;
}
export type ProfileUncheckedCreateInput = {
id?: number;
/* ... */
userId?: number;
photoId?: number;
}
Which basically means that you either provide all references as connect, create etc. relations (checked) or as raw ids (unchecked). You cannot mix the styles, this is not supported.
Related
I've got an odd error, I've been googling to no end and have been unable to figure this out.
I've got a prisma.schema looking like this;
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Account {
id String #id #default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? #db.Text
access_token String? #db.Text
expires_at Int?
token_type String?
scope String?
id_token String? #db.Text
session_state String?
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
##unique([provider, providerAccountId])
}
model Session {
id String #id #default(cuid())
sessionToken String #unique
userId String
expires DateTime
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String #id #default(cuid())
name String?
email String #unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String #unique
expires DateTime
##unique([identifier, token])
}
and a addUser.ts file looking like this...
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import { User, PrismaClient } from '#prisma/client';
import type { NextApiRequest, NextApiResponse } from 'next';
type Data = {
name: string;
};
const prisma = new PrismaClient();
export default async(req: NextApiRequest, res: NextApiResponse) => {
const createUser: User = await prisma.user.create({
data:{
name: `${Math.random() * 10000}`,
email: `${Math.random() * 10000}#gmail.com`,
}
});
res.json('user created succesfully');
}
Trouble is when I run the code I get this error....
Error:
Invalid `prisma.user.create()` invocation:
{
data: {
name: '8096.003938770688',
email: '8343.394432092025#gmail.com'
~~~~~
}
}
Unknown arg `email` in data.email for type UserCreateInput. Did you mean `name`? Available args:
type UserCreateInput {
name: String
}
But if I look at the UserCreateInput type (I haven't made any changes to this file, I've used prisma generate to match my schema when I've made changes to it) I can see this....
export type UserCreateInput = {
id?: string
name?: string | null
email: string
emailVerified?: Date | string | null
image?: string | null
accounts?: AccountCreateNestedManyWithoutUserInput
sessions?: SessionCreateNestedManyWithoutUserInput
}
If I comment out the line email: ${Math.random() * 10000}#gmail.com, and set the schema to "email String? #unique" it works fine and creates the entry into the DB as expected, so it seems to be something with my email field but I can't figure out what.
I have been following multiple guides but none of them seem to expect this issue or have any resolutions for it.
I should point out, I am fairly new to development, and I'm not a developer by trade, so this is more of a learning thing for me :)
Appreciate your time guys
I have a form where clients can register for contests. They submit their name and email address, and the registration gets stored in a table in the database which has a composite primary key of the contestId and the clientId.
If someone submits the form a second time (i.e., tries to register for the same contest a second time), Prisma throws a failed constraint error. That's fine, and even expected, but I'd like to return the existing entry, and am having a hard time constructing the query.
Schema:
model Client {
id Int #id #default(autoincrement())
email String #unique
first String?
last String?
registrations Registration[]
}
model Contest {
id Int #id #default(autoincrement())
name String #unique
registrations Registration[]
}
model Registration {
contest Contest #relation(fields: [contestId], references: [id])
contestId Int
contestant Client #relation(fields: [clientId], references: [id])
clientId Int
points Int
##id ([contestId, clientId])
}
Registration:
try {
const registration = await prisma.registration.create({
data: {
points: 1,
contest: {
connectOrCreate: {
where: {
name: contest,
},
create: {
name: contest,
}
}
},
contestant: {
connectOrCreate: {
where: {
email: email,
},
create: {
first: first,
last: last,
email: email,
},
},
},
}
});
return res.status(201).send({ ...registration});
}
For new registrants that works, but if the registration already exists, I end up in the catch block. I assume this is the right way to do this — as opposed to, say, querying for existence first, because that seems expensive given that it's likely to be very rare that someone accidentally tries to re-register — but if this isn't a best practice on how to handle things, I'm open to other suggestions.
In the catch block, I then need to find the existing entry, but neither of the two things I've tried have worked:
catch (error) {
// entry already exists
if ('P2002' === error.code) {
const registration = await prisma.registration.findUnique({
where: {
contest: {
is: {
name: contest,
},
},
contestant: {
is: {
email: email,
},
},
},
})
return res.status(200).send(...registration);
}
return res.status(500).end(`Register for contest errorr: ${error.message}`);
}
complains Argument where of type RegistrationWhereUniqueInput needs exactly one argument, but you provided contest and contestant.
And
const registration = await prisma.registration.findUnique({
where: {
contestId_clientId: {
contest: {
is: {
name: contest,
},
},
contestant: {
is: {
email: email,
},
},
},
},
})
complains
Unknown arg `contest` in where.contestId_clientId.contest for type RegistrationContestIdClientIdCompoundUniqueInput. Did you mean `contestId`?
Unknown arg `contestant` in where.contestId_clientId.contestant for type RegistrationContestIdClientIdCompoundUniqueInput. Did you mean `contestId`?
Argument contestId for where.contestId_clientId.contestId is missing.
Argument clientId for where.contestId_clientId.clientId is missing.
I feel like this latter approach of using Prisma's auto-generated contestId_clientId is directionally right, but how do I construct the Prisma query to find it, starting from having the contest name and client email?
Instead of finding a unique record from the registration model, you can do a query something like this to find the Contest and Client details. The only valid argument for findUnique in the registration model would be: contestId_clientId and you can only pass contestId and clientId in them, other values are not allowed.
const registration = await prisma.client.findUnique({
where: {
email: 'email',
},
include: {
registrations: {
select: {
contest: contest,
points: true,
},
},
},
});
I have the following database model:
Each Mediablock contains a reference to exactly one UTS object and one Media object.
Each UTS object contains rawText and normalisedText
Each Media object contains a url and a timestamp
My schema.prisma looks like this:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Mediablock {
id String #id #default(auto()) #map("_id") #db.ObjectId // gen a new, unique id
UTS UTS #relation(fields: [utsId], references: [id])
utsId String #unique #map("uts_id") #db.ObjectId
Media Media #relation(fields: [mediaId], references: [id])
mediaId String #unique #map("media_id") #db.ObjectId
}
model UTS {
id String #id #default(auto()) #map("_id") #db.ObjectId
rawText String
normalisedText String
createdAt DateTime #default(now())
Mediablock Mediablock?
}
// // Mediablocks contain a Video object and connect back to the Mediablock.
// // mediablockId must have #db.ObjectId to match up with Mediablock's id type
model Media {
id String #id #default(auto()) #map("_id") #db.ObjectId
url String
createdAt DateTime #default(now())
Mediablock Mediablock?
}
My resolvers look like this:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
//This resolver retrieves mediabooks from the "mediabooks" array above.
module.exports = {
Query: {
allMediablocks: () => prisma.mediablock.findMany(),
allMedia: () => prisma.media.findMany(),
allUTS: () => prisma.uts.findMany(),
},
};
And my typedefs looks like this:
module.exports = `
type Mediablock {
id: ID!
uts: UTS
media: Media # can be null when the text is generated first
}
type UTS {
id: ID!
rawText: String!
normalisedText: String!
}
type Media {
id: ID!
url: String!
createdAt: String!
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "allMediablocks" query returns an array of zero or more Mediablocks (defined above).
type Query {
allMediablocks: [Mediablock]
allMedia: [Media]
allUTS: [UTS]
}
`;
My seed file looks like this:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
const mediaData = [
{
UTS: {
create: {
rawText: 'Welcome',
normalisedText: 'welcome',
},
},
Media: {
create: {
url: 'https://www.youtube.com/watch?v=kq9aShH2Kg4',
createdAt: '2022-09-29T12:00:00.000Z',
}
}
}
];
async function main() {
console.log(`Started seeding ...`);
for (const d of mediaData) {
const mediablock = await prisma.Mediablock.create({
data: d,
});
console.log(`Created Mediablock with id: ${mediablock.id}`);
}
console.log(`\nSeeding complete.`);
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
My problem is that when I attempt to query allMediablocks, I can't get any of the UTS or Media data.
query allMediaBlocks {
allMediablocks {
uts {
normalisedText
}
media {
url
}
}
}
// response
{
"data": {
"allMediablocks": [
{
"uts": null,
"media": null
}
]
}
}
I just get null values for both, when in fact, the database (MongoDB) contains references to both of these objects in other tables.
What am I doing wrong? Are my resolvers incorrect?
Is my schema structured incorrectly for MongoDB?
I've fixed it by changing my schema.prisma to be:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Mediablock {
id String #id #default(auto()) #map("_id") #db.ObjectId // gen a new, unique id
utsId String #unique #map("uts_id") #db.ObjectId
uts UTS #relation(fields: [utsId], references: [id])
mediaId String #unique #map("media_id") #db.ObjectId
media Media #relation(fields: [mediaId], references: [id])
}
model UTS {
id String #id #default(auto()) #map("_id") #db.ObjectId
rawText String
normalisedText String
createdAt DateTime #default(now())
mediablock Mediablock?
}
// // Mediablocks contain a Video object and connect back to the Mediablock.
// // mediablockId must have #db.ObjectId to match up with Mediablock's id type
model Media {
id String #id #default(auto()) #map("_id") #db.ObjectId
url String
createdAt DateTime #default(now())
mediablock Mediablock?
}
My resolver to be:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
//This resolver retrieves mediabooks from the "mediabooks" array above.
module.exports = {
Query: {
allMediablocks: () => prisma.mediablock.findMany({
include: {
media: true,
uts: true
}
}),
allMedia: () => prisma.media.findMany(),
allUTS: () => prisma.uts.findMany(),
},
};
And my typedefs to be:
module.exports = `
type Mediablock {
id: ID!
uts: UTS
media: Media # can be null when the text is generated first
}
type UTS {
id: ID!
rawText: String!
normalisedText: String!
}
type Media {
id: ID!
url: String!
createdAt: String!
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "allMediablocks" query returns an array of zero or more Mediablocks (defined above).
type Query {
allMediablocks: [Mediablock]
allMedia: [Media]
allUTS: [UTS]
}
`;
I also changed the way my seed function works:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
const mediaData = [
{
uts: {
create: {
rawText: 'Welcome',
normalisedText: 'welcome',
createdAt: '2022-09-29T12:00:00.000Z',
},
},
media: {
create: {
url: 'https://www.youtube.com/watch?v=kq9aShH2Kg4',
createdAt: '2022-09-29T12:00:00.000Z',
}
}
}
];
async function main() {
console.log(`Started seeding ...`);
for (const d of mediaData) {
const mediablock = await prisma.Mediablock.create({
data: d,
});
console.log(mediablock);
}
console.log(`\nSeeding complete.`);
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
Now, using the same GraphQL query:
query allMediaBlocks {
allMediablocks {
uts {
normalisedText
}
media {
url
}
}
}
I get the following (desired) response:
{
"data": {
"allMediablocks": [
{
"uts": {
"normalisedText": "welcome"
},
"media": {
"url": "https://www.youtube.com/watch?v=kq9aShH2Kg4"
}
}
]
}
}
I encountered a problem while trying to seed the user table.
I have a one-to-one relationship between the User and UserSettings model in it.
async function generateUsers() {
const users = [];
for (let i = 0; i < randomUsersCount; i++) {
users[i] = {
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
email: faker.internet.email(),
password: await hashPassword('testtest'),
phone: faker.phone.phoneNumber('###-###-###'),
role: 'USER',
is_blocked: false,
user_settings : {
create: {
language: 'PL',
color: faker.internet.color(),
}
}
};
}
await prisma.user.createMany({
data: users,
});
}
Error message:
Unknown arg `user_settings` in data.0.user_settings for type UserCreateManyInput. Available args:
type UserCreateManyInput {
id?: Int
first_name: String
last_name: String
email: String
phone: String
password: String
role: UserRoles
is_blocked?: Boolean
created_at?: DateTime
updated_at?: DateTime
}
but in my model the field exists:
model User {
id Int #id #default(autoincrement())
first_name String
last_name String
email String #unique
phone String
password String
role UserRoles
is_blocked Boolean #default(false)
created_at DateTime #default(now())
updated_at DateTime #updatedAt
user_settings UserSettings?
}
model UserSettings {
id Int #id #default(autoincrement())
language Languages
color String
user_id Int
user User #relation(fields: [user_id], references: [id])
}
When I turn on Prisma Studio, I can see the column present in the table.
Where did I go wrong?
I have run npx prisma generate and npx prisma migrate and even npx prisma migrate reset many times, without success.
You are trying to use nest createMany which isn't supported.
This is from the documentation:
You cannot create or connect relations - you cannot nest create,
createMany, connect, connectOrCreate inside a top-level createMany
As an alternative I would suggest to create users inside the loop while you are creating data.
async function generateUsers() {
const users = [];
for (let i = 0; i < randomUsersCount; i++) {
users[i] = {
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
email: faker.internet.email(),
password: await hashPassword('testtest'),
phone: faker.phone.phoneNumber('###-###-###'),
role: 'USER',
is_blocked: false,
user_settings : {
create: {
language: 'PL',
color: faker.internet.color(),
}
}
};
await prisma.user.create({
data: users[i],
});
}
}
I am new to Prisma.
I am trying to use Prisma with Nextjs.
But I am facing problems with one to many relations.
I have a schema like below.
model Thread {
id Int #id #default(autoincrement()) #unique
title String
kakikoes Kakiko[]
}
model Kakiko {
id Int #id #default(autoincrement()) #unique
thread Thread #relation(fields: [threadId], references: [id])
threadId Int
}
// Kakiko means "Post" in Japanese.
There is "one to many Relation" which towards "kakiko" from Thread.
I can get datas from DB and I can send datas to browser, But My TS compiler says "TS2339: Property 'kakikoes' does not exist on type 'Thread'."
await prisma.thread.findUnique({
where: {
id: threadId
},
include: {
kakikoes: true
}
})
{thread.kakikoes.map(kakiko => <p>{kakiko.body}</p>)}
I checked type definition, and kakikoes not defined.
/**
* Model Thread
*
*/
export type Thread = {
id: number
itaId: number
title: string
}
How can I solve this?
Thank you.
Added (2022/04/03)
I use Intelij Idea Ultimate.
Type is defined like this.
My code is working correctly on My browser And Nextjs dev Tool doesnt say Any Errors nor Any Warnings.
The model is correct. I have tried it and this is the working solution.
schema.prisma file
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Thread {
id Int #id #unique #default(autoincrement())
title String
kakikoes Kakiko[]
}
model Kakiko {
id Int #id #unique #default(autoincrement())
thread Thread #relation(fields: [threadId], references: [id])
threadId Int
}
main.ts file
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
async function main() {
// ... you will write your Prisma Client queries here
await prisma.kakiko.create({
data: {
thread: {
create: {
title: 'test',
},
},
},
});
const thread = await prisma.thread.findUnique({
where: {
id: 1,
},
include: {
kakikoes: true,
},
});
console.log('thread', thread);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
Here is the response
thread { id: 1, title: 'test', kakikoes: [ { id: 1, threadId: 1 } ] }
Attaching the image as well:
I would also suggest restarting your vs code typescript compiler which could cause this issue.