prismaprisma2

Unknown argument error when creating record


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?


Solution

  • 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.