I keep getting a TypeORM error indicating a null value in a column that violates a not-null constraint. Can someone help me figure out what mistake I'm making

I've been utilizing TypeORM to develop a task system that involves various entities such as owning committee, related committees, project leads, and employee entries.

My expectation is to pass a unit test successfully with the following code execution:

const taskDetails: TaskDetails = {
            title: "Baz Creation",
            startDate: new Date(),
            endDate: new Date(),
            committeeId: newCommittee1.committeeId,
        };
        const taskPayload1 = {
            committees: [newCommittee1, newCommittee2],
            projectLead: newUser1,
            membersToAdd: [newUser1, newUser2, newUser3],
        };
        await taskDAO.createTask(newCommittee1, taskDetails, taskPayload1.committees, taskPayload1.projectLead, taskPayload1.membersToAdd);

However, an error is being thrown, stating:

   QueryFailedError: null value in column "memberUserId" of relation "tasks_for_member" violates not-null constraint

      at PostgresQueryRunner.query (src/driver/postgres/PostgresQueryRunner.ts:299:19)
      at InsertQueryBuilder.execute (src/query-builder/InsertQueryBuilder.ts:163:33)
      at SubjectExecutor.executeInsertOperations (src/persistence/SubjectExecutor.ts:428:42)
      at SubjectExecutor.execute (src/persistence/SubjectExecutor.ts:137:9)
      at EntityPersistExecutor.execute (src/persistence/EntityPersistExecutor.ts:197:21)
      at TaskDAO.createTask (src/db/dao/task.dao.ts:49:13)
      at Object.<anonymous> (tests/dbUtil/purgeDb.test.ts:132:9)

The error message suggests that there should be an entry in the join table, but it seems like I am unable to make TypeORM create that entry correctly.

I'm confused because:

(a) TypeORM should handle creating entries in the join table automatically. (b) All relations in my database are set to be nullable. See below for details.

Below are snippets from my Task and Member entity files:

Task.ts

@Entity()
export class Task {
    @PrimaryGeneratedColumn()
    taskId: number;

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

    @Column({ nullable: true })
    status: Role;

    @ManyToMany(() => Member, (member: Member) => member.tasks, { nullable: true, onDelete: "CASCADE" })
    @JoinTable({ name: "task_leads" })
    leads?: Member[];

    @ManyToMany(() => Member, (member: Member) => member.tasks, { nullable: true, onDelete: "CASCADE" })
    @JoinTable({ name: "task_members" })
    members?: Member[];

    @ManyToOne(() => Committee, committee => committee.inChargeOf, { nullable: true })
    @JoinColumn({ name: "owning_committee" }) 
    ownedBy?: Committee;

    @ManyToMany(() => Committee, committee => committee.tasks, { nullable: true, onDelete: "CASCADE" })
    @JoinTable({ name: "task_committees" })
    relatedCommittees?: Committee[];
}

Member.ts:

@Entity()
export class Member {
    @PrimaryGeneratedColumn()
    userId: number;

    @Column({ nullable: true })
    displayName?: string;


    @ManyToMany(() => Committee, committee => committee.members, { nullable: true, onDelete: "CASCADE" })
    @JoinTable({ name: "member_of" })
    memberOf?: Committee[];

    @ManyToMany(() => Committee, committee => committee.leads, { nullable: true, onDelete: "CASCADE" })
    @JoinTable({ name: "lead_of" })
    leadOf?: Committee[];

    @OneToMany(() => Committee, committee => committee.head, { nullable: true, onDelete: "CASCADE" })
    headOf?: Committee;

    @ManyToMany(() => Task, (task: Task) => task.members, { nullable: true, onDelete: "CASCADE" })
    @JoinTable({ name: "tasks_for_member" }) 
    tasks?: Task[];
}

This is how I'm creating the task:

public async createTask(
        headCommittee: Committee,
        taskDetails: TaskDetails,
        relatedCommittees: Committee[] | null,
        projectLead: Member | null,
        membersToAdd: Member[] | null,
    ): Promise<Task> {
        try {
            const task = new Task();
            task.title = taskDetails.title;
            task.startDate = taskDetails.startDate;
            task.endDate = taskDetails.endDate;
            task.ownedBy = headCommittee;
            if (projectLead) {
                task.leads = [projectLead];
            }
            if (membersToAdd) {
                task.members = membersToAdd;
                for (const member of membersToAdd) { 
                    member.tasks = [task];
                    this.memberRepository.save(member);
                } 
            }
            if (relatedCommittees) {
                task.relatedCommittees = relatedCommittees;
            }
            console.log(task, "36rm");
            await this.taskRepository.save(task);
            return task;
        } catch (error: unknown) {
            // handle
        }
    }

In light of the issue mentioned, any insights on how to address it would be much appreciated.

Please let me know your thoughts! Thank you.

Answer №1

Finally cracked the code!

After some trial and error, I discovered that having @JoinTable on both sides of the Task <=> Committee and Task <=> Member relationship was causing issues.

It turns out that it should only be present on I believe one side of the relationship, but don't take my word for it, check out the documentation for more details

@Entity()
export class Album {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @ManyToMany(() => Photo, (photo) => photo.albums)
    @JoinTable() // make sure to see this line
    photos: Photo[]
}

export class Photo {
    // ... other properties

    @ManyToMany(() => Album, (album) => album.photos)
    albums: Album[] // ensure that @JoinTable is NOT included here
}
const photo = new Photo()
photo.name = "Exploring the Wilderness"
photo.description = "Capturing moments with wildlife"
photo.filename = "nature-photography.jpg"
photo.views = 1
photo.isPublished = true
photo.albums = [album1, album2] // pay attention here - assigning the albums
await AppDataSource.manager.save(photo)

Photo uses the JoinTable because it's where the albums are linked to. If the scenario involved attaching photos to albums like album.photos = [photo1, photo2], then the @JoinTable would belong on the opposite side

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

Error: Unable to access the 'secondary' property of an undefined object - encountered after updating Material-UI from version 4 to version 5

We recently completed an upgrade from MUI version 4 to version 5, and since the upgrade, our UI tests have been failing with the following error: TypeError: Cannot read property 'secondary' of undefined (I added comment to which line in code thi ...

Allow Visual Studio Code to create a constructor for Typescript class

When developing Angular 2 apps in Typescript using Visual Studio Code, one common task is writing constructors with their parameter list. Is there a way to save time and effort on this? It would be really helpful if the IDE could automatically generate th ...

How can I inquire about restricted relationships using typeorm's OneToMany relationships?

When it comes to adding messages under conversations, I prefer using OneToMany relations. Currently, I am facing an issue where I need to query a conversation with only the latest messages. Here's an example of how I am trying to achieve this- const c ...

Angular2 app fails to update after emitting an event

I need help with a child component responsible for updating phone numbers on a webpage. The goal is for the application to automatically display the changed phone number once the user hits the 'save' button. Here's a visual of how the appli ...

A missing Array.at() method has been reported in TypeScript array type

When I try: const myArray = [0,4,2]; myArray.at(-1); I encounter the following error related to .at The error message reads: Property 'at' does not exist on type 'number[]'.ts (2339) Why is Array.at() not working in this case? Is th ...

Using Typescript for Asynchronous Https Requests

I've been attempting all day to make an https request work. My current code isn't functioning as expected; when I run it, I encounter an "Unhandled error RangeError: Maximum call stack size exceeded at Function.entries" import * as https from &q ...

Encountering an error with the ".ts" file extension when attempting to execute a ts-node script

I am currently facing an issue while trying to execute a script that consists of two .ts files in a regular folder. One file contains the script, and the other has helper functions required to run it. Additionally, I am importing external libraries such as ...

Is there a way to implement personalized error management in TypeScript with Express?

For a while now, I have been using JavaScript to create my APIs but recently made the switch to TypeScript. However, I keep encountering errors along the way. One particular error handler I have set up is for when a route cannot be found, as shown below: i ...

Why did the compilation of Next.js using TypeScript and ESLint succeed despite encountering errors?

I've been delving into Next.js and encountered unexpected results when integrating TypeScript and ESLint. ESLint seems to work well with TypeScript, but my project compilation is successful despite encountering errors. It's puzzling why the comp ...

Utilizing Regex to Authenticate a CAGE Code

Our task is to create a RegEx pattern that can accurately validate a CAGE Code A CAGE Code consists of five (5) positions. The code must adhere to the following format: The first and fifth positions must be numeric. The second, third, and fourth position ...

Using TypeScript to define attributes by merging specified attribute names with variable attribute names

Can a TypeScript type/interface be created with the specified structure below? interface Model { id: number; something: string; somethingElse: Date; [key: string]: string | null; } It essentially consists of both defined attributes and 0 to n und ...

The selected image should change its border color, while clicking on another image within the same div should deselect the previous image

https://i.sstatic.net/jp2VF.png I could really use some assistance! I've been working on Angular8 and I came across an image that shows how all the div elements are being selected when clicking on an image. Instead of just removing the border effect f ...

Utilizing class-validator for conditional validation failure

Implementing conditional validation in the class-validator library using the given example, I need to ensure that validation fails if the woodScrews property is assigned a value when the tool property is set to Tool.TapeMeasure. I've searched extensiv ...

Using AngularJS with CDN: A beginner's guide

If I need to create an app using AngularJS with Cordova in Visual Studio, do I need anything else besides the Google CDN for AngularJS? <!doctype html> <html ng-app> <head> <title>My Angular App</title> <script s ...

Having trouble with the clip-path in d3.js liquid fill gauge

Attempting to integrate the d3.js liquid fill gauge into my angular2 webapp has been a challenge. The clippath functionality seems to be malfunctioning, resulting in no wave being generated at all. https://i.stack.imgur.com/3Bmga.png instead of https://i. ...

Converting a Promise to an Observable in Angular using Typescript

I have a working method that functions as intended: getdata(): Promise<any> { let query = `SELECT * FROM table`; return new Promise((resolve, reject) => { this.db.query(query, (error, rows) => { if(error) reject(error); ...

A guide to implementing vue-i18n in Vue class components

Take a look at this code snippet: import Vue from 'vue' import Component from 'vue-class-component' @Component export default class SomeComponent extends Vue { public someText = this.$t('some.key') } An error is being thr ...

Found a minor syntax problem in an Angular service related to error handling declaration

As I was working on customizing the Angular tutorial to fit my needs, I found myself wanting to merge the two error handler methods showcased in the tutorial into one. I appreciate the functionality of both methods and believe combining them will be benefi ...

A mistake has occurred: Unhandled promise rejection TypeError: Unable to assign the property 'devices' to an undefined object in Ionic 4 with Angular

Within my MyDevicesPage class, I am attempting to manipulate the res object and then pass it to the updateDevicesToServer method of DataService for further actions. The code compiles without errors, but at runtime, an error is thrown: ERROR Error: Uncaught ...

Is there any need for transpiling .ts files to .js when Node is capable of running .ts files directly?

If you are using node version 12, try running the following command: node hello.ts I'm curious about the purpose of installing typescript globally with npm: npm install -g typescript After that, compiling your TypeScript file to JavaScript with: ...