#Struggling with MikroORM and interceptors

1 messages · Page 1 of 1 (latest)

hearty tapir
#

Hi,

I have two entities that have a many to many relations between them, when I do role.users.add(user) (with users initialized) the collection is updated, but after that, if I interact with the role or user entity it seems that MikroORM is trying to perform an update request against the users table with invalid data (trying to set the email field to NULL while it is non-nullable)

// Controller
export class RolesController {
    constructor(private readonly rolesService: RolesService) {}

    @Post(':role_id/users/:user_id')
    async addUserToRole(@Param('role_id') role_id: number, @Param('user_id') user_id: number) {
        return this.rolesService.addUser(role_id, user_id);
    }
}
// Service
export class RolesService {
    constructor(private readonly orm: MikroORM) {}

    @UseRequestContext()
    async addUser(role_id: number, user_id: number): Promise<BaseUserResponseDTO[]> {
        const role = await this.orm.em.findOne(Role, { id: role_id });
        if (!role) throw new NotFoundException(`Role with ID ${role_id} not found`);

        const user = await this.orm.em.findOne(User, { id: user_id });
        if (!user) throw new NotFoundException(`User with ID ${user_id} not found`);

        await role.users.init();
        role.users.add(user);
        await this.orm.em.persistAndFlush(role);

        const res: BaseUserResponseDTO[] = [];
        role.users.getItems().forEach((user) =>
            res.push({
                id: user.id,
                updated_at: user.updated_at,
                created_at: user.created_at,
                first_name: user.first_name,
                last_name: user.last_name,
                nickname: user.nickname,
            }),
        );

        return res;
    }
}
@Entity({ tableName: 'users' })
export class User {
    // ...

    @Property({ unique: true })
    email: string;

    /** Linked roles to the user */
    @ManyToMany(() => Role, (role) => role.users)
    roles = new Collection<Role>(this);
}
@Entity({ tableName: 'roles' })
export class Role {
    // ...

    /** Specify to which user the role is attached */
    @ManyToMany(() => User, (user) => user.roles, { owner: true })
    users = new Collection<User>(this);
}
#

It is in fact, cause by an interceptor that I use to create a log each time a request is made :

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
    constructor(private readonly orm: MikroORM) {}

        @UseRequestContext()
    async intercept(context: ExecutionContext, next: CallHandler<unknown>): Promise<Observable<unknown>> {
        const request = context.switchToHttp().getRequest();
        const user_id = request.user ? request.user.id : 'Guest';

        // No need to log guest users
        if (user_id === 'Guest') return next.handle();

        // Create a new log entry
        const user = await this.orm.em.findOne(User, { id: user_id });
        const log = this.orm.em.create(Log, {
            user,
            action: context.getClass().name + '.' + context.getHandler().name,
            ip: context.switchToHttp().getRequest().ip,
            user_agent: context.switchToHttp().getRequest().headers['user-agent'],
            route: context.switchToHttp().getRequest().route.path,
            method: context.switchToHttp().getRequest().method,
            body: context.switchToHttp().getRequest().body,
            query: context.switchToHttp().getRequest().query,
            params: context.switchToHttp().getRequest().params,
            updated_at: undefined,
        });

        return next.handle().pipe(
            tap({
                finalize: async () => {
                    // Update the log entity after the observable is ended
                    log.response = context.switchToHttp().getResponse().body;
                    log.status_code = context.switchToHttp().getResponse().statusCode;
                    log.error = context.switchToHttp().getResponse().error;
                    log.error_stack = context.switchToHttp().getResponse().error_stack;
                    log.error_message = context.switchToHttp().getResponse().error_message;
                    log.updated_at = new Date();

                    await this.orm.em.flush(); // <- this line breaks everything
                },
            }),
        );
    }
}

Does anybody have an Idea how I can update the log after the request has been made ? 🤔

#

Here is the update that MikroORM attempt :

#

And the stacktrace :

E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\postgresql\PostgreSqlExceptionConverter.js:24 
                return new core_1.NotNullConstraintViolationException(exception);
                       ^
NotNullConstraintViolationException: update "users" set "email" = NULL, "updated_at" = '2023-06-28T00:10:02.435Z' where "id" = 1 - null value in column "email" of relation "users" violates not-null constraint    
    at PostgreSqlExceptionConverter.convertException (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\postgresql\PostgreSqlExceptionConverter.js:24:24)
    at PostgreSqlDriver.convertException (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\drivers\DatabaseDriver.js:197:54)
    at E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\drivers\DatabaseDriver.js:201:24  
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at PostgreSqlDriver.nativeUpdate (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected][email protected]\node_modules\@mikro-orm\knex\AbstractSqlDriver.js:345:19)
    at ChangeSetPersister.persistManagedEntity (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\unit-of-work\ChangeSetPersister.js:137:21)
    at ChangeSetPersister.executeUpdates (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\unit-of-work\ChangeSetPersister.js:42:13)
    at ChangeSetPersister.runForEachSchema (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\unit-of-work\ChangeSetPersister.js:68:13)
    at UnitOfWork.commitUpdateChangeSets (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\unit-of-work\UnitOfWork.js:789:9)
    at UnitOfWork.persistToDatabase (E:\GitHub\AE\api\node_modules\.pnpm\@[email protected]_@[email protected]_@[email protected]_@[email protected]\node_modules\@mikro-orm\core\unit-of-work\UnitOfWork.js:707:13)

previous error: update "users" set "email" = NULL, "updated_at" = '2023-06-28T00:10:02.435Z' where "id" = 1 - null value in column "email" of relation "users" violates not-null constraint
    at Parser.parseErrorMessage (E:\GitHub\AE\api\node_modules\.pnpm\[email protected]\node_modules\pg-protocol\src\parser.ts:369:69)
    at Parser.handlePacket (E:\GitHub\AE\api\node_modules\.pnpm\[email protected]\node_modules\pg-protocol\src\parser.ts:188:21)
    at Parser.parse (E:\GitHub\AE\api\node_modules\.pnpm\[email protected]\node_modules\pg-protocol\src\parser.ts:103:30)
    at Socket.<anonymous> (E:\GitHub\AE\api\node_modules\.pnpm\[email protected]\node_modules\pg-protocol\src\index.ts:7:48)
    at Socket.emit (node:events:513:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at Socket.Readable.push (node:internal/streams/readable:228:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
    at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
hearty tapir
#

Found out I'm just dumb aha_oups