Search code examples
graphqlnestjs

Nestjs-query library, how to set mandatory relation while we create new one entity getting error: QueryFailedError: null value in column creatorId


I'm trying to figure out with Nestjs-query library and faced with the case when I need to create for the Task many-to-one relation with the Creator while saving my task entity, at the same time I don't want to receive userId from FE and taking creatorId from the request context my mutation looks like :

  @Mutation(() => TaskDto)
  @UseGuards(GqlAuthGuard)
  async createTaskWithAllDetails(@Args('data') data: TaskInputDto, @CurrentUser() user) {
    const { projectId, markIds, reminderId } = data;
    data.creatorId = user.id;
    
    const task = await this.serviceTask.createOne(data);

    if (projectId) {
      await this.fillProjectData(task.id, projectId);
    }
    if (markIds) {
      await this.fillMarksData(task.id, markIds);
    }
    if (reminderId) {
      await this.fillRemindersData(task.id, reminderId);
    }
    return task;
  }

  private async fillProjectData(taskId, projectId): Promise<void> {
    const project = await this.projectService.findById(projectId);
    await this.serviceTask.setRelation(
      'project',
      taskId,
      project.id
    );
  }

@CurrentUser decorator gives me current user data and I put in in DTO, so before I successful create relations (project, mark, reminder) throw provided methods from Nestjs-query QueryService and used the setRelation method, but for this method, we have to save entity before and then we could call this method and send new one created entity id and entity for relation, its looks fine for optional relations only, when I'm trying to send only creatorId value I getting error: QueryFailedError: null value in column "creatorId" of relation "task_entity" violates not-null constraint

my task entity looks like:

    @Entity()
@Unique(['name', 'id'])
export class TaskEntity extends BaseEntity {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @ManyToOne(() => ProjectEntity, (project) => project.tasks,
    { nullable: true })
  project: ProjectEntity;

  @Column({ nullable: true })
  deadline: Date;

  @ManyToOne(() => MarkEntity, (mark) => mark.tasks,
    { nullable: true })
  marks: MarkEntity;

  @OneToOne(() => ReminderEntity, (reminder) => reminder.task,
    { nullable: true })
  remind: ReminderEntity;

  @Column({ default: StatusesEnum.relevant })
  status: StatusesEnum;

  @ManyToOne(() => UserEntity, (user) => user.id,
    { nullable: true })
  creator: UserEntity;
}

my DTO for the task looks like this:

@ObjectType('task')
@Authorize({ authorize: (context: UserContext) => ({ creatorId: { eq: context.req.user.id } }) })
@Relation('creator', () => UserDto, { disableRemove: true, nullable: false })
@Relation('project', () => ProjectDto, { disableRemove: true, nullable: true })
@Relation('priority', () => MarkDto, { disableRemove: true, nullable: true })
@Relation('reminder', () => ReminderDto, { disableRemove: true, nullable: true })
export class TaskDto {
  @Field(type => ID)
  id: string;

  @FilterableField({ nullable: true })
  name: string;

  @FilterableField({ nullable: true })
  description: string;

  @FilterableField({ nullable: true })
  deadline: Date;

  @FilterableField(() => ID, { nullable: true })
  priorityId!: string;

  @FilterableField(() => ID,{ nullable: true })
  projectId!: string;

  @FilterableField(() => ID, { nullable: true })
  reminderId!: string;

  @FilterableField()
  @IsEnum(StatusesEnum)
  status: StatusesEnum;

  @FilterableField(() => ID, { nullable: false })
  creatorId: string;
}

So the question is - how we could save mandatory relation at the same time with entity creation, throw this library Nestjs-query by using the setRelation or setRelations methods its fit only for optional relations which always require that entities were created before it


Solution

  • If somebody like me is faced with the same issue, follow the documentation best approach is to use hooks in DTO And due to the fact that I have used JWT tokens simplest way is to getting user from request context and make it like this:

        @BeforeUpdateOne((input: UpdateOneInputType<TaskDto>, context) => {
          input.update.creator = context.user; 
        return input;})
      @BeforeCreateOne((input: UpdateOneInputType<TaskDto>, context) => {
          input.update.creator = context.user; 
        return input;})