loopbackio/loopback-next

Error in the "Dynamically adding models, repositories, and controllers during runtime" section

Closed this issue · 16 comments

There is a section "Dynamically adding models, repositories, and controllers during runtime" https://loopback.io/doc/en/lb4/Dynamic-models-repositories-controllers.html in the docs where we can find a way to add a model to API during runtime.

But. Following these steps I cannot create a CRUD controller with fields described in a model definition

    const bookDef = new ModelDefinition({
      name: 'book',
      properties: {
        id: {
          type: 'Number',
          required: true,
          id: 1,
        },
        title: {
          type: 'String',
          required: false,
        },
      },
    });

    const BookModel = defineModelClass<typeof Entity, {id: number; title?: string}>(
      Entity,
      bookDef,
    );

    const BookRepository = defineCrudRepositoryClass(BookModel);

    inject(`datasources.mongo`)(BookRepository, undefined, 0);
    const repoBinding = app.repository(BookRepository);

    const basePath = '/booktest';

    const DynamicController0 = defineCrudRestController(BookModel, {basePath});
    inject(repoBinding.key)(DynamicController0, undefined, 0);
    app.controller(DynamicController0);

CRUD controller created successfully but with no params (actually, only with params of the base Entity/Model).

Снимок экрана 2020-07-24 в 14 15 31

Expected that API could add object Book with params id and title

There are no errors or warnings in console.

Where are you executing that code? Can you share a bare minimal app reproducing the behavior?

This is a working example, add it in the constructor of your Application class. Please make sure to import the necessary dependencies.

    @model()
    class Product extends Entity {
      @property({id: true})
      id: number;

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

      @property()
      description?: string;

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

    const db = new juggler.DataSource({connector: 'memory'});
    const ProductRepository = defineCrudRepositoryClass(Product);
    const repo = new ProductRepository(db);

    const CrudRestController = defineCrudRestController<
      Product,
      typeof Product.prototype.id,
      'id'
    >(Product, {basePath: '/products'});

    class ProductController extends CrudRestController {
      constructor() {
        super(repo);
      }
    }

    this.controller(ProductController);

Thanks, but it's not suitable for me. I have to do it via ModelDefinition (that would be created dynamically) because in the real app structure of my model (Product in your example) is not available on application startup. Exactly that way described in the docs (but do not work). In other words, I'am trying to create a new model by extending Entity at runtime.

I will provide minimal demo app ASAP.

Here it is: https://github.com/kvbogdanov/monopolia-test1

Two noticeable things added to the minimal app:

  1. https://github.com/kvbogdanov/monopolia-test1/blob/master/src/dynamic-models.ts - runtime model and controller creation logic (there the main problem is)
  2. Added action /ping/build in default ping controller: https://github.com/kvbogdanov/monopolia-test1/blob/master/src/controllers/ping.controller.ts

How it works: there is only one Ping controller at app startup. But if you send get request to /ping/build new CRUD controller booktest would be created (for the runtime-created model BookModel). But there are no any fields still (id and title expected)

Снимок экрана 2020-07-27 в 11 11 18

Снимок экрана 2020-07-27 в 11 26 36

@kvbogdanov thanks for the sample app. I am taking a look at it.

It is happening because getModelSchemaRef() is not generating the schema. Tomorrow I will discuss with @bajtos and come up with a plan.

I am facing the same issue. I use my-sql in my project. The schema properties are not displaying in explorer. But was the table created here?. The DB table was not created in my case.

A fix is landing!

@hacksparrow, is this issue good to close? Thanks.

@hacksparrow I adopted the fix and I am following https://loopback.io/doc/en/lb4/Dynamic-models-repositories-controllers.html. But the db tables were not created still. I tried the code from @kvbogdanov above. Thank you

I will re-retry tomorrow, but yesterday after updating lb4 I'd still facing the problem

Thank you, I have managed to create the db tables by calling autoupdate after defining the repository now.

I did lb4 update and npm update but example still doesn't work

@hacksparrow tell me please where am I wrong?

@kvbogdanov the fix has landed on master, but it is not published yet. In the mean time you can do this:

  1. Replace monopolia-test1/node_modules/@loopback/repository with https://github.com/strongloop/loopback-next/tree/master/packages/repository.
  2. Run npm i and then npm run build in the monopolia-test1/node_modules/@loopback/repository dir.
  3. Start app as usual and see the changes.

@hacksparrow, is it good to close? Thanks.

Fixed in #6036.