Error: Unable to cast value to an array due to validation failure

I'm currently working on integrating Typegoose with GrqphQL, MongoDB, and Nest.js for a project. My goal is to create a mutation that will allow users to create a post. I have set up the model, service, and resolver for a simple Post. However, when I attempt to run the mutation to create a Post, I encounter the following error:

PostModel validation failed: sections: Cast to Array failed for value:

[
  [Object: null prototype] {
    title: 'section 1',
    body: [ 'section test lalal' ],
    code: [ "console.log('hello world!)" ],
    image: [ 'kanow.svg' ]
  }
]

at path "sections"

I have tried using ref and itemsRefs for both class and string values. You can find more information about this in the Typegoose arrayProp documentation. After creating a new PostModel and logging every property, I noticed that the sections array is empty, even though it shouldn't be. The JSON within postInput looks like this:

[Object: null prototype] {
  title: 'refactored post',
  image: 'rest.jpg',
  tags: [ 'ref, ref' ],
  sections: [
    [Object: null prototype] {
      title: 'section 1',
      body: [Array],
      code: [Array],
      image: [Array]
    }
  ]
}

Based on this JSON data, everything seems correct, so this should not be the cause of the error.

I would appreciate any insights on what I might be doing wrong and why it's not working as expected. Below you'll find some relevant code snippets. Feel free to ask if you need any additional information in the comments.

GraphQL mutation:

mutation {
  createPost(postInput: {
    image: "rest.jpg",
    title: "refactored post"
    tags: ["ref, ref"]
    sections: [{
      title: "section 1"
      body: ["section test lalal"]
      code: ["console.log('hello world!)"]
      image: ["kanow.svg"]
    }]
  }) {
    _id
    title
    tags
    sections {
      title
      body
      code
      image
    }
  }
}

post.service.ts:

@Injectable()
export class PostsService {
    constructor(@InjectModel(PostModel) private readonly postModel: ReturnModelType<typeof PostModel>) {
    }

    async create(postInput: CreatePostInput): Promise<DocumentType<PostModel>> {
        const createdPost: DocumentType<PostModel> = new this.postModel(postInput);
        return await createdPost.save();
    }
 ...
}

post.model.ts:

@ObjectType()
export class PostModel {
    @Field(() => ID)
    readonly _id: ObjectId;

    @Field()
    @prop({required: true})
    title: string;

    @Field()
    @prop({nullable: true})
    image: string;

    @Field(() => [String])
    @arrayProp({items: String})
    tags: string[];

    @Field(() => [SectionModel])
    @arrayProp({ref: 'SectionModel'})
    sections: Ref<SectionModel>[];
}

section.model.ts:

@ObjectType()
export class SectionModel {
  @Field()
  @prop({ required: true })
  title: string;

  @Field(() => [String])
  @arrayProp({ items: String })
  body: string[];

  @Field(() => [String])
  @arrayProp({ items: String })
  code: string[];

  @Field(() => [String])
  @arrayProp({ items: String })
  image: string[];
}

create-post.input.ts:

@InputType()
export class CreatePostInput {
  @Field()
  readonly title!: string;

  @Field()
  readonly image!: string;

  @Field(() => [String])
  readonly tags!: string[];

  @Field(() => [CreateSectionInput])
  readonly sections!: CreateSectionInput[];
}

UPDATE 1

I found that passing an empty array inside the sections body allows me to create a post without any issues. Here's an example query:

mutation {
  createPost(postInput: {
    image: "newly created.jpg",
    title: "newly created"
    tags: ["newly created, ref"]
    sections: []
  }) {
    _id
    image
    title
    tags
    sections {
      title
      body
      code
      image
    }
  }
}

Answer №1

After some research, I realized that using Ref<> to declare nested documents was not the right approach. Instead, you need to utilize items and specify the correct Typegoose model class. While the documentation initially states that items are intended only for primitive types, it later clarifies that you can also use them with Typegoose classes (such as SectionModel.ts). Below is an example that effectively resolves this issue.

post.model.ts:

@ObjectType()
export class PostModel {
  @Field(() => ID)
  readonly _id: ObjectId;

  @Field()
  @prop({required: true})
  title: string;

  @Field()
  @prop({nullable: true})
  image: string;

  @Field(() => [String])
  @arrayProp({items: String})
  tags: string[];

  @Field(() => [SectionModel])
  @arrayProp({items: SectionModel})
  sections: SectionModel[];
}

section.model.ts:

@ObjectType()
export class SectionModel {
  @Field(() => ID)
  readonly _id: ObjectId;

  @Field()
  @prop({ required: true })
  title: string;

  @Field(() => [String])
  @arrayProp({ items: String })
  body: string[];

  @Field(() => [String])
  @arrayProp({ items: String })
  code: string[];

  @Field(() => [String])
  @arrayProp({ items: String })
  image: string[];
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is a more precise way to define an Object type on a temporary basis?

I'm currently working with Angular 2. Typically, when I specify a type, I start by creating an interface: interface Product { name: string, count: number } and then use it like this: let product: Product; However, now I need to temporarily de ...

What is the best way to download a file with a specific name using Angular and TypeScript?

Greetings! Below is a snippet of code from my Angular component: this.messageHistoryService.getMessageHistoriesCSV1(msgHistoryRequest).subscribe( (data) => { console.log(data.messageHistoryBytes); let file = new Blob( [data.messageHistoryBytes ...

The proper order for logging in is to first validate the login credentials before attempting

I created a custom validation class to verify if a user is logged in before allowing them access to a specific page. However, after implementing this validation, my program no longer routes me to the intended component. Validation.ts export class UserVal ...

Issue with TypeScript in Vue3: Unable to access computed property from another computed property

In my Vue3 project with TypeScript, I am encountering an issue where I am unable to access the properties of the returned JavaScript object from one computed property in another computed property using dot notation or named indexing. For instance, when tr ...

The useParams() function encounters difficulty in converting data to number typescript

Whenever I try to convert the heroId type from string to number in my code, I always encounter an error. Here is the code snippet: import useHero from '../../hooks/useHero'; import {useParams} from 'react-router-dom' function Herospag ...

What is the best way to retry an action stream observable in Angular/RxJS after it fails?

Kindly disregard the variable names and formatting alterations I've made. I've been attempting to incorporate RxJS error handling for an observable that triggers an action (user click) and then sends the request object from our form to execute a ...

"Fixing the cubic-bezier for the exiting animation ends up causing issues with the entering

Trying to implement a collapsing list animation using React/Joy-UI. Below is the Transition element code snippet: <Transition nodeRef={nodeRef} in={browseOpen} timeout={1000}> {(state: string) => (<List aria-labelledby="nav-list-bro ...

Consolidate type definition within a tsx file using VS Code

Whenever I define a type in a TypeScript file (.ts) on VS Code, I notice that there is no option to collapse the definition. Here's an example: export type Test = { someValue: string, someOtherValue: string, yetAnotherValue: string }; I ...

How can one specify a type in Typescript with a precise number of properties with unspecified names?

Imagine I have a variable with a name and a value, both of which I need for a specific task such as logging. This can be achieved in the following way: const some_variable = "abcdef" const another_variable = 12345 const log1 = (name: string, value: any) ...

Efficiently Updating Property Values in Objects Using TypeScript and Loops

I have been looking into how to iterate or loop through a code snippet, and while I managed to do that, I am facing an issue where I cannot change the value of a property. Here is the snippet of code: export interface BaseOnTotalPaidFields { groupName ...

The 'checked' property cannot be bound to 'mat-button-toggle' as it is not recognized as a valid property in Angular 9

I am encountering an issue with my Angular 9 application. I have integrated angular-material and imported the MatCheckboxModule correctly in the module. Here is the version of the material package I am using: "@angular/material": "^10.2.0&q ...

Angular 2: Utilizing Http Subscribe Method with "this" Context Pointer

Question: http.request('js/app/config/config.json').subscribe(data => { this.url = data.json().url; }); It seems that "this" is pointing to Subscriber instead of the parent class. I was under the impression that the fat- ...

Is React 18 compatible with both react-redux and react-router?

At present, my react application is running on the following versions: react 17.0.x react-dom 17.0.x react-redux 7.2.x react-router-dom 5.x.x react-scripts 4.0.x redux 4.x.x My initial step towards upgrading to react@18 involved updating react-scripts to ...

Enhancing current interfaces

I'm exploring Koa and the module system in Node.js. Although I'm not asking about a specific koa question, all the code I'm working with involves using koa. In Koa, every request is defined by the Request interface: declare module "koa" { ...

Change the spread operator in JavaScript to TypeScript functions

I'm struggling to convert a piece of code from Javascript to Typescript. The main issue lies in converting the spread operator. function calculateCombinations(first, next, ...rest) { if (rest.length) { next = calculateCombinations(next, ...res ...

Mongoose managing diverse connections

Currently, I am working with Node.Js 8.6 and Mongoose 4.11, managing multiple database connections using mongoose.createConnection. Upon exploring the connections property within the mongoose object (an array that stores established connections), I am curi ...

Chaining Assignments in TypeScript

let a: { m?: string }; let b = a = {}; b.m = ''; // Property 'm' does not exist on type '{}'. let a: { m?: string } = {}; let b = a; b.m = ''; // It's OK Link to TypeScript Playground What occurs ...

Typescript error: The property 'set' is not found on type '{}'

Below is the code snippet from my store.tsx file: let store = {}; const globalStore = {}; globalStore.set = (key: string, value: string) => { store = { ...store, [key]: value }; } globalStore.get = (key) => { return store[key]; } export d ...

Error encountered: `npm ERR! code E503`

While attempting to execute npm install on my project, which was cloned from my GitHub repository, I encountered the following error: npm ERR! code E503 npm ERR! 503 Maximum threads for service reached: fs-extra@https://registry.npmjs.org/fs-extra/-/fs-ex ...

Determine the number of populated elements in a Mongoose and ExpressJS application

I need help creating pagination for reviews of specific items. I am utilizing mongoose to populate reviews for movies. movies.findOne({_id:req.params.id}).populate('review') Is there a way to retrieve the total count of populated reviews? ...