mongodbnext.jscrudprismaserver-action

What is Causing the Payload Argument must be type object when data matches the schema


I am getting an error

 ⨯ [TypeError: The "payload" argument must be of type object. Received null] {
  code: 'ERR_INVALID_ARG_TYPE',
  digest: '447529059'
}

when I have the following prisma server action (using next.js for context)

'use server'

import {PrismaClient} from '@prisma/client'
import {NewLesson} from "@/app/(portal)/admin/courses/[slug]/lessons/[lslug]/types/lesson";
import {revalidatePath} from 'next/cache'

const prisma = new PrismaClient()

const createOrUpdateLesson = async (data: NewLesson, cId: string, aId: string) => {
    const {id, authorId, courseId, additionalResources, files, validations, ...rest} = data

    try {
        const course = await prisma.course.findFirstOrThrow({
            where: {id: cId},
            select: {slug: true}
        })
        const vConnectOrCreate = validations.map((v) => {
            return {
                where: {id: v.id},
                create: {
                    value: v.value,
                    message: v.message,
                    type: v.type
                }
            }
        })
        const fConnectOrCreate = files.map((f) => {
            return {
                where: {id: f.id},
                create: {
                    name: f.name,
                    type: f.type,
                    content: f.content
                }
            }
        })
        const rConnectOrCreate = additionalResources.map((a) => {
            return {
                where: {id: a.id},
                create: {
                    type: a.type,
                    title: a.title,
                    value: a.value,
                }
            }
        })
        const lesson = await prisma.lesson.upsert({
            where: {slug: data.slug},
            update: {
                ...rest,
                Author: {connect: {id: aId}},
                Course: {connect: {id: cId}},
                Validations: {
                    connectOrCreate: vConnectOrCreate
                },
                Files: {
                    connectOrCreate: fConnectOrCreate
                },
                AdditionalResources: {
                    connectOrCreate: rConnectOrCreate
                }
            },
            create: {
                ...rest,
                Author: {connect: {id: aId}},
                Course: {connect: {id: cId}},
                Validations: {
                    connectOrCreate: vConnectOrCreate
                },
                Files: {
                    connectOrCreate: fConnectOrCreate
                },
                AdditionalResources: {
                    connectOrCreate: rConnectOrCreate
                }
            },
            include: {Validations: true, AdditionalResources: true, Files: true}
        })
        revalidatePath(`/courses/${course.slug}/lessons/${lesson.slug}`, 'page')
        return {success: true, lesson}
    } catch (error) {
        console.error('Failed to create/update lesson:', error)
        return {success: false, error: 'Failed to create/update course'}
    }
}

export {createOrUpdateLesson}

I have the following schema

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

model Account {
  id                String  @id @default(auto()) @map("_id") @db.ObjectId
  userId            String  @db.ObjectId
  user              User    @relation(fields: [userId], references: [id])
  type              String
  provider          String
  providerAccountId String
  refresh_token     String?
  access_token      String?
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String?
  sesssion_state    String?
}

model Session {
  id           String   @id @default(auto()) @map("_id") @db.ObjectId
  sessionToken String   @unique
  userId       String   @db.ObjectId
  user         User     @relation(fields: [userId], references: [id])
  expires      DateTime
}

model VerificationToken {
  id         String   @id @default(auto()) @map("_id") @db.ObjectId
  identifier String
  token      String   @unique
  expires    DateTime
}

model User {
  id                String           @id @default(auto()) @map("_id") @db.ObjectId
  name              String?
  login             String           @unique
  email             String           @unique
  password          String           @unique
  emailVerified     DateTime?
  image             String?
  accounts          Account[]
  Articles          Article[]
  Comments          Comment[]
  Sessions          Session[]
  enrolledCourseIds String[]         @db.ObjectId
  membership        Membership?      @relation(fields: [membershipId], references: [id])
  membershipId      String?          @db.ObjectId
  roleId            String?          @db.ObjectId
  role              Role?            @relation(fields: [roleId], references: [id])
  BillingAddresses  BillingAddress[]
  AuthoredCourses   Course[]         @relation("Author")
  EnrolledCourses   Course[]         @relation("Students", fields: [enrolledCourseIds], references: [id])
  Lessons           Lesson[]
  Doc               Doc[]
}

model Comment {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  content   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  authorId  String   @db.ObjectId
  author    User     @relation(fields: [authorId], references: [id])
  articleId String   @db.ObjectId
  article   Article  @relation(fields: [articleId], references: [id])
}

model Article {
  id          String    @id @default(auto()) @map("_id") @db.ObjectId
  title       String    @unique
  slug        String    @unique
  content     String
  image       String?
  description String?
  comments    Comment[]
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  authorId    String    @db.ObjectId
  author      User      @relation(fields: [authorId], references: [id])
  categoryId  String    @db.ObjectId
  category    Category  @relation(fields: [categoryId], references: [id])
}

model Category {
  id       String    @id @default(auto()) @map("_id") @db.ObjectId
  name     String    @unique
  articles Article[]
  courses  Course[]
}

model Feature {
  id              String           @id @default(auto()) @map("_id") @db.ObjectId
  name            String           @unique
  MembershipTypes MembershipType[]
}

model MembershipType {
  id          String       @id @default(auto()) @map("_id") @db.ObjectId
  name        String       @unique
  price       Int
  duration    Int
  descrition  String
  featuresIds String[]     @db.ObjectId
  features    Feature      @relation(fields: [featuresIds], references: [id])
  Memberships Membership[]
}

model Membership {
  id        String         @id @default(auto()) @map("_id") @db.ObjectId
  typeId    String         @db.ObjectId
  type      MembershipType @relation(fields: [typeId], references: [id])
  createdAt DateTime       @default(now())
  updatedAt DateTime       @updatedAt
  Users     User[]
}

model Role {
  id    String @id @default(auto()) @map("_id") @db.ObjectId
  name  String @unique
  users User[]
}

model BillingAddress {
  id         String  @id @default(auto()) @map("_id") @db.ObjectId
  line1      String
  line2      String?
  city       String
  state      String?
  postalCode String?
  country    String
  email      String  @unique
  userId     String  @db.ObjectId
  user       User    @relation(fields: [userId], references: [id])
}

model Course {
  id          String    @id @default(auto()) @map("_id") @db.ObjectId
  title       String    @unique
  description String
  slug        String    @unique
  categoryId  String?   @db.ObjectId
  category    Category? @relation(fields: [categoryId], references: [id])
  authorId    String    @db.ObjectId
  studentIds  String[]  @db.ObjectId
  Author      User      @relation(name: "Author", fields: [authorId], references: [id])
  Students    User[]    @relation(name: "Students", fields: [studentIds], references: [id])
  lessons     Lesson[]
}

enum ValidationType {
  ELEMENT_CONTAINS          @map("Element Contains")
  USED_FUNCTION             @map("Used Function")
  CREATED_ELEMENT           @map("Created Element")
  HAS_CODE_BLOCK            @map("Has Code Block")
  HAS_EXACT_CODE            @map("Has Exact Code")
  CREATED_COMMENT           @map("Created Comment")
  USED_VARIABLE             @map("Used Variable")
  USED_IMPORT               @map("Used Import")
  USED_HOOK                 @map("Used Hook")
  IMPLEMENTED_INTERFACE     @map("Implemented Interface")
  EXTENDED_CLASS            @map("Extended Class")
  USED_STATE_MANAGEMENT     @map("Used State Management")
  IMPLEMENTED_EVENT_HANDLER @map("Implemented Event Handler")
}

enum AdditionalResourceType {
  BLOG          @map("blog")
  DOC           @map("doc")
  EXTERNAL_LINK @map("external link")
}

model Validation {
  id       String         @id @default(auto()) @map("_id") @db.ObjectId
  lessonId String         @db.ObjectId
  Lesson   Lesson         @relation(fields: [lessonId], references: [id])
  type     ValidationType @default(ELEMENT_CONTAINS)
  value    String
  message  String?
}

model AdditionalResource {
  id       String                 @id @default(auto()) @map("_id") @db.ObjectId
  type     AdditionalResourceType
  lessonId String                 @db.ObjectId
  Lesson   Lesson                 @relation(fields: [lessonId], references: [id])
  title    String                 @unique
  value    String
}

enum FileType {
  JAVASCRIPT @map("js")
  TYPESCRIPT @map("ts")
  CSS        @map("css")
  HTML       @map("html")
}

model File {
  id       String   @id @default(auto()) @map("_id") @db.ObjectId
  type     FileType
  name     String
  content  String
  lessonId String   @db.ObjectId
  Lesson   Lesson   @relation(fields: [lessonId], references: [id])
}

model Lesson {
  id                  String               @id @default(auto()) @map("_id") @db.ObjectId
  title               String
  description         String
  step                Int
  slug                String               @unique
  courseId            String               @db.ObjectId
  Course              Course               @relation(fields: [courseId], references: [id])
  authorId            String               @db.ObjectId
  Author              User                 @relation(fields: [authorId], references: [id])
  duration            Int
  content             String
  Validations         Validation[]
  AdditionalResources AdditionalResource[]
  Files               File[]
}

model Doc {
  id          String @id @default(auto()) @map("_id") @db.ObjectId
  title       String @unique
  slug        String @unique
  description String
  content     String
  authorId    String @db.ObjectId
  Author      User   @relation(fields: [authorId], references: [id])
}

I was expecting the system to save/update the lesson along with the needed resources and create the additional records or update them as needed.

I have been trying to use different upserts, for loops, and even trying to get it to tell me what data is invalid. My typescript is not seeing any issues with the data being passed either from the client component or in the script itself. I have been trying to do this so its as simple and easy to look at as possible.


Solution

  • Log error with console.log(error.stack) to show prisma error. console.log(error) don't show exact error message.

    In my case ,It show connector error, Prisma load enviroment variables from .env.local when I retrieve or create. So I set database's env variables in .env.local too and resolved the error.