Home > Software engineering >  Type 'Promise<Course>[]' is not assignable to type 'Course[]'
Type 'Promise<Course>[]' is not assignable to type 'Course[]'

Time:05-22

I want to create a many-to-many relationship between two objects - courses and students. I use MongoDB. When creating a new student I want to be able to add the courses. In createStudent mutation I am calling the query for getting the course which I defined in Course Resolver, but the following error occurs:

Type 'Promise[]' is not assignable to type 'Course[]'. Type 'Promise' is missing the following properties from type 'Course': _id, title, description, subject, and 3 more.

When creating a new student, I want to pass the courses id as a string array. Then in the mutation method I would like to map these ids to the Course objects from the database and return an array of courses which I want to pass to the student data. How can I do this?

Here is the student entity:

@ObjectType()
export class Student {
    @Field()
    readonly _id: ObjectId;

    @Field()
    @Prop({ required: true })
    name: string;

    @Field()
    @Prop({ required: true, unique: true })
    email: string;

    @Field()
    @Prop({ required: true })
    password: string;

    @Field()
    @Prop({ required: true, unique: true })
    facultyNumber: number;

    @Field()
    @Prop({ default: Date.now() })
    lastLogin?: number;

    @Field(type => [Course])
    @ManyToMany(() => Course, course => course._id, { cascade: true })
    @JoinTable({ name: "student_courses",joinColumn: { name: "student_id" }, inverseJoinColumn: { name: "course_id" } })
    @Prop({default: []})
    courses?: Course[]

}

export const StudentModel = getModelForClass(Student, { schemaOptions: { timestamps: true } });

Courses entity:

@ObjectType()
export class Course {
  @Field()
  readonly _id: ObjectId;

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

  @Field()
  @Prop({ required: true })
  description: string;

  @Field()
  @Prop({ required: true })
  subject: string;

  @Field()
  @Prop({ required: true })
  credits: number;

  @Field()
  @Prop({ required: true })
  tutor: string;

  @Field(() => [Student])
  @ManyToMany(() => Student, student => student._id, { cascade: true})
  students!: Student[]
}

export const CourseModel = getModelForClass(Course);

The input arguments for student creation:

@InputType({ description: "New student data" })
export class StudentInput {

    @Field()
    @MaxLength(50)
    name: string;

    @Field()
    @IsEmail()
    @MaxLength(30)
    email: string;

    @Field()
    @MinLength(6)
    password: string;

    @Field()
    @IsInt()
    @IsPositive()
    @Min(1000000000)
    facultyNumber: number;

    @Field(type => [String], { nullable: true})
    coursesIDs?: string[]
}

Student resolver:

@Resolver()
export class StudentResolver {
    
    @Mutation(returns => Student)
    async createStudent(@Arg("data") data: StudentInput): Promise<Student> {
        const a: CourseResolver = new CourseResolver();

        const courses: Course[] = data.coursesIDs.map((id) => {
            return Promise.resolve(a.course(id));
        });
        const studentData = { ...data, password: bcryptjs.hashSync(data.password, 5) }
        const newStudent = new StudentModel(studentData);
        newStudent.courses = courses;
        await newStudent.save();
        return newStudent;
    }
}

Course resolver:

@Resolver()
export class CourseResolver {
    @Query(returns => Course)
    async course(@Arg("_id") _id: string): Promise<Course> {
        return await CourseModel.findById(_id);
    }
}

CodePudding user response:

I solved it by using then() and pushing the response I get from the Promise to the courses array.

@Mutation(returns => Student)
    async createStudent(@Arg("data") data: StudentInput): Promise<Student> {
        const a: CourseResolver = new CourseResolver();

        const studentData = { ...data, password: bcryptjs.hashSync(data.password, 5) }
        const newStudent = new StudentModel(studentData);
        data.coursesIDs.map((id) => {
            a.course(id).then((res) => {
                newStudent.courses.push(res);
            });
        });
        await newStudent.save();
        return newStudent;
    }

CodePudding user response:

This is not related to your question about the promise, but your setup is a bit strange.

Resolvers are like controllers which contain methods (Queries/Mutations) to resolve the data for the fields on your GraphQL schema.

You shouldn't create new instances of other resolvers into these methods. Instead, you should create shared services, which you inject into the resolvers. The actual resolving of the data should be the responsibility of this service (preferably through repositories). Also, in order to write tests for your code, dependency injection is really necessary.

These are few of the SOLID Design Principles of Object Oriented Design.

  • Related