2

I am getting into Loopback 4, coming from Sequelize and with a bit of experience with TypeORM.

My question is why in Loopback 4 the relations doesn't get constraints. I have seen https://loopback.io/doc/en/lb4/todo-list-tutorial-sqldb.html#specify-the-foreign-key-constraints-in-todo-model, but I don't get why it doesn't do it automatically, or if I'm doing something wrong.

Lets see an example with the same models for the three ORMS: a User model that can have many Post:

User --* Post

Sequelize:

User:

    const User = sequelize.define(
        'User',
        {
            id: {
                type: Sequelize.INTEGER,
                autoIncrement: true,
                primaryKey: true,
                allowNull: false,
            },

            name: {
                type: Sequelize.STRING,
            },
        },
        {}
    );

    User.associate = function(models) {
        User.hasMany(models.Post, { onDelete: 'CASCADE' });
    };

Post:

    const Post = sequelize.define(
        'Post',
        {
            id: {
                type: Sequelize.INTEGER,
                autoIncrement: true,
                primaryKey: true,
                allowNull: false,
            },

            title: {
                type: Sequelize.STRING,
            },

            text: {
                type: Sequelize.TEXT,
            },
        },
        {}
    );

    Post.associate = function(models) {
        Post.belongsTo(models.User, { onDelete: 'CASCADE' });
    };

Result in MYSQL:


    CREATE TABLE IF NOT EXISTS `test_sequelize`.`Users` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(255) NULL DEFAULT NULL,

      PRIMARY KEY (`id`))
    ENGINE = InnoDB
    AUTO_INCREMENT = 3
    DEFAULT CHARACTER SET = utf8
    COLLATE = utf8_unicode_ci;

    CREATE TABLE IF NOT EXISTS `test_sequelize`.`Posts` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `title` VARCHAR(255) NULL DEFAULT NULL,
      `text` TEXT NULL DEFAULT NULL,

      `UserId` INT(11) NULL DEFAULT NULL,
      PRIMARY KEY (`id`),
      INDEX `UserId` (`UserId` ASC) ,
      CONSTRAINT `posts_ibfk_1`
        FOREIGN KEY (`UserId`)
        REFERENCES `test_sequelize`.`Users` (`id`)
        ON DELETE CASCADE
        ON UPDATE CASCADE)
    ENGINE = InnoDB
    AUTO_INCREMENT = 4
    DEFAULT CHARACTER SET = utf8
    COLLATE = utf8_unicode_ci;

Ok, the constraints are there.

Now lets try with TypeORM:

TypeORM:

User:


    import { Post } from './Post';

    @Entity()
    export class User {
      @PrimaryGeneratedColumn()
      id: string;

      @Column()
      name: string;

      @OneToMany(type => Post, post => post.user, { onDelete: 'CASCADE' })
      posts: Post[];
    }

Post:


    import { User } from './User';
    import { PostHasTag } from './PostHasTag';
    import { Tag } from './Tag';

    @Entity()
    export class Post {
      @PrimaryGeneratedColumn()
      id: number;

      @Column()
      title: string;

      @Column({ type: 'text' })
      text: string;

      @ManyToOne(type => User, user => user.posts)
      user: User;
    }

And this is the result in MySQL:


    CREATE TABLE IF NOT EXISTS `test_typeorm`.`user` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(255) NOT NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB
    AUTO_INCREMENT = 3
    DEFAULT CHARACTER SET = utf8
    COLLATE = utf8_unicode_ci;


    CREATE TABLE IF NOT EXISTS `test_typeorm`.`post` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `title` VARCHAR(255) NOT NULL,
      `text` TEXT NOT NULL,
      `userId` INT(11) NULL DEFAULT NULL,
      PRIMARY KEY (`id`),
      INDEX `FK_5c1cf55c308037b5aca1038a131` (`userId` ASC) ,
      CONSTRAINT `FK_5c1cf55c308037b5aca1038a131`
        FOREIGN KEY (`userId`)
        REFERENCES `test_typeorm`.`user` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB
    AUTO_INCREMENT = 4
    DEFAULT CHARACTER SET = utf8
    COLLATE = utf8_unicode_ci;

Everything ok, constraints there. Now with Loopback 4.

Loopback 4:

The models, repositories, controllers and relations are generated by the cli, but I will only post the models:

User:


    import {Post} from './post.model';

    @model({settings: {}})
    export class User extends Entity {
      @property({
        type: 'number',
        id: true,
        generated: true,
      })
      id?: number;

      @property({
        type: 'string',
        required: true,
      })
      name: string;

      @hasMany(() => Post)
      posts: Post[];

      constructor(data?: Partial<User>) {
        super(data);
      }
    }

    export interface UserRelations {
      // describe navigational properties here
    }

    export type UserWithRelations = User & UserRelations;

Post:


    import {Entity, model, property} from '@loopback/repository';

    @model({settings: {}})
    export class Post extends Entity {
      @property({
        type: 'number',
        id: true,
        generated: true,
      })
      id?: number;

      @property({
        type: 'string',
        required: true,
      })
      title: string;

      @property({
        type: 'string',
      })
      text?: string;

      @property({
        type: 'number',
      })
      userId?: number;

      constructor(data?: Partial<Post>) {
        super(data);
      }
    }

    export interface PostRelations {
      // describe navigational properties here
    }

    export type PostWithRelations = Post & PostRelations;

And the MySQL:


    CREATE TABLE IF NOT EXISTS `test_loopback`.`Post` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `title` VARCHAR(512) NOT NULL,
      `text` VARCHAR(512) NULL DEFAULT NULL,
      `userId` INT(11) NULL DEFAULT NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB
    AUTO_INCREMENT = 2
    DEFAULT CHARACTER SET = utf8
    COLLATE = utf8_unicode_ci;

    CREATE TABLE IF NOT EXISTS `test_loopback`.`User` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(512) NOT NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB
    AUTO_INCREMENT = 2
    DEFAULT CHARACTER SET = utf8
    COLLATE = utf8_unicode_ci;

And as you may see, there are no constraints there. Am I doing something wrong? Is this expected behaviour?

EmileC
  • 39
  • 4

2 Answers2

1

This is a known short-coming of LoopBack, see the discussion in the following GitHub issues:

Miroslav Bajtoš
  • 10,667
  • 1
  • 41
  • 99
  • Thanks @miroslavbajtos; I'm starting to understand that LoopBack 4 is not a finished product. I don't know why they published it, but its half done. Will try again with it in six months or so. – EmileC Oct 06 '19 at 12:52
  • Asked it here: https://stackoverflow.com/questions/58257457/loopback-4-why-it-was-published-if-its-not-a-finished-product – EmileC Oct 06 '19 at 12:58
0

Loopback 4 and constraints in MySQL relations

@model({
  settings: {
    foreignKeys: {
      fk_todo_todoListId: {
        name: 'fk_todo_todoListId',
        entity: 'TodoList',
        entityKey: 'id',
        foreignKey: 'todolistid',
      },
    },
  },
})
export class Todo extends Entity {
  //etc.
}

https://loopback.io/doc/en/lb4/todo-list-tutorial-sqldb.html#specify-the-foreign-key-constraints-in-todo-model

аlex
  • 5,426
  • 1
  • 29
  • 38