Search code examples
postgresqltypescriptloopback

Loopback 4 - HasMany relation included in fields


I am trying to setup the relation HasMany with the new Loopback 4 framework. I have the following model:

import {Entity, model, property, belongsTo, hasMany} from 

'@loopback/repository';
import {User} from "./user.model";
import {OrderProduct} from "./order-product.model";

@model({
  name: 'sales_order'
})
export class Order extends Entity {
  @property({
    type: 'number',
    id: true,
    required: true,
  })
  id: number;

  @property({
    type: 'number',
    required: true,
  })
  total_amount: number;

  @belongsTo(() => User)
  user_id: number;

  @hasMany(() => OrderProduct, {keyTo: 'order_id'})
  products?: OrderProduct[];

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

And the repository as follow:

import {DefaultCrudRepository, repository, HasManyRepositoryFactory, BelongsToAccessor} from '@loopback/repository';
import {Order, OrderProduct, User} from '../models';
import {DbDataSource} from '../datasources';
import {inject, Getter} from '@loopback/core';
import {OrderProductRepository} from "./order-product.repository";
import {UserRepository} from "./user.repository";

export class OrderRepository extends DefaultCrudRepository<
  Order,
  typeof Order.prototype.id
> {

  public readonly user: BelongsToAccessor<
      User,
      typeof Order.prototype.id
    >;

  public readonly products: HasManyRepositoryFactory<
      OrderProduct,
      typeof Order.prototype.id
  >;

  constructor(
    @inject('datasources.db') dataSource: DbDataSource,
    @repository.getter(OrderProductRepository)
        getOrderProductRepository: Getter<OrderProductRepository>,
    @repository.getter('UserRepository')
        userRepositoryGetter: Getter<UserRepository>,
  ) {
    super(Order, dataSource);

    this.products = this._createHasManyRepositoryFactoryFor(
        'products',
        getOrderProductRepository,
    );

    this.user = this._createBelongsToAccessorFor(
        'user_id',
        userRepositoryGetter,
    );
  }
}

When I do for example a get on orders, I have the errors: 500 error: column "products" does not exist and in digging a bit more, I can see that the SQL is trying to retrieve the fields products where it is just a relation. Anybody have an idea if I am doing something wrong? I am using pg as DB.


Solution

  • I believe this is a bug in LoopBack 4. When you decorate a class property with @hasMany, the decorator defines a model property under the hood. See here:

    export function hasMany<T extends Entity>(
      targetResolver: EntityResolver<T>,
      definition?: Partial<HasManyDefinition>,
    ) {
      return function(decoratedTarget: Object, key: string) {
        property.array(targetResolver)(decoratedTarget, key);
    
        // ...
      };
    }
    

    When the connector is querying the database, it's trying to include the column products in the query, because it thinks products is a property.

    The problem is already tracked by https://github.com/strongloop/loopback-next/issues/1909, please consider upvoting the issue and joining the discussion.