LoopBack 4 TodoList Application Tutorial - Add TodoList Repository
Page Contents

One great feature of a related model’s repository is its ability to expose a factory function (a function that returns a newly instantiated object) to return a ‘constrained’ version of the related model’s repository. A factory function is useful because it allows you to create a repository whose operations are limited by the data set that applies to the factory function.

In this section, we’ll build TodoListRepository to have the capability of building a constrained version of TodoRepository.

Create your repository

From inside the project folder, run the lb4 repository command to create a repository for the TodoList model using the db datasource. The db datasource shows up by its class name DbDataSource from the list of available datasources.

lb4 repository
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a repository TodoList
   create src/repositories/todo-list.repository.ts
   update src/repositories/index.ts
? Please select the repository base class DefaultCrudRepository (Legacy juggler
bridge)

Repository TodoListRepository was created in src/repositories/

From there, we’ll need to make two more additions:

  • define the todos property, which will be used to build a constrained TodoRepository
  • inject TodoRepository instance

Once the property type for todos has been defined, use this.createHasManyRepositoryFactoryFor to assign it a repository constraining factory function. Pass in the name of the relationship (todos) and the Todo repository instance to constrain as the arguments for the function.

src/repositories/todo-list.repository.ts

import {Getter, inject} from '@loopback/core';
import {
  DefaultCrudRepository,
  HasManyRepositoryFactory,
  juggler,
  repository,
} from '@loopback/repository';
import {Todo, TodoList, TodoListRelations} from '../models';
import {TodoRepository} from './todo.repository';

export class TodoListRepository extends DefaultCrudRepository<
  TodoList,
  typeof TodoList.prototype.id,
  TodoListRelations
> {
  public readonly todos: HasManyRepositoryFactory<
    Todo,
    typeof TodoList.prototype.id
  >;

  constructor(
    @inject('datasources.db') dataSource: juggler.DataSource,
    @repository.getter(TodoRepository)
    protected todoRepositoryGetter: Getter<TodoRepository>,
  ) {
    super(TodoList, dataSource);
    this.todos = this.createHasManyRepositoryFactoryFor(
      'todos',
      todoRepositoryGetter,
    );
  }
}

To get the related Todo object for each TodoList, we have to override the find and findById functions.

First add the following imports:

import {Filter, Options} from '@loopback/repository';
import {TodoListWithRelations} from '../models';

Add the following two functions after the constructor:

src/repositories/todo-list.repository.ts

async find(
  filter?: Filter<TodoList>,
  options?: Options,
): Promise<TodoListWithRelations[]> {
  // Prevent juggler for applying "include" filter
  // Juggler is not aware of LB4 relations
  const include = filter && filter.include;
  filter = {...filter, include: undefined};
  const result = await super.find(filter, options);

  // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  // and use `inq` operator to fetch related todos in fewer DB queries
  // this is a temporary implementation, please see
  // https://github.com/strongloop/loopback-next/issues/3195
  if (include && include.length && include[0].relation === 'todos') {
    await Promise.all(
      result.map(async r => {
        r.todos = await this.todos(r.id).find();
      }),
    );
  }

  return result;
}

async findById(
  id: typeof TodoList.prototype.id,
  filter?: Filter<TodoList>,
  options?: Options,
): Promise<TodoListWithRelations> {
  // Prevent juggler for applying "include" filter
  // Juggler is not aware of LB4 relations
  const include = filter && filter.include;
  filter = {...filter, include: undefined};

  const result = await super.findById(id, filter, options);

  // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  // and use `inq` operator to fetch related todos in fewer DB queries
  // this is a temporary implementation, please see
  // https://github.com/strongloop/loopback-next/issues/3195
  if (include && include.length && include[0].relation === 'todos') {
    result.todos = await this.todos(result.id).find();
  }

  return result;
}

Now when you get a TodoList, a todos property will be included that contains your related Todos, for example:

{
  "id": 2,
  "title": "My daily chores",
  "todos": [
    {
      "id": 3,
      "title": "play space invaders",
      "desc": "Become the very best!",
      "todoListId": 2
    }
  ]
}

Let’s do the same on the TodoRepository:

src/repositories/todo.repository.ts

async find(
  filter?: Filter<Todo>,
  options?: Options,
): Promise<TodoWithRelations[]> {
  // Prevent juggler for applying "include" filter
  // Juggler is not aware of LB4 relations
  const include = filter && filter.include;
  filter = {...filter, include: undefined};

  const result = await super.find(filter, options);

  // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
    // and use `inq` operator to fetch related todo-lists in fewer DB queries
    // this is a temporary implementation, please see
    // https://github.com/strongloop/loopback-next/issues/3195
  if (include && include.length && include[0].relation === 'todoList') {
    await Promise.all(
      result.map(async r => {
        r.todoList = await this.todoList(r.id);
      }),
    );
  }

  return result;
}

async findById(
  id: typeof Todo.prototype.id,
  filter?: Filter<Todo>,
  options?: Options,
): Promise<TodoWithRelations> {
  // Prevent juggler for applying "include" filter
  // Juggler is not aware of LB4 relations
  const include = filter && filter.include;
  filter = {...filter, include: undefined};

  const result = await super.findById(id, filter, options);

  // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  // and use `inq` operator to fetch related todo-lists in fewer DB queries
  // this is a temporary implementation, please see
  // https://github.com/strongloop/loopback-next/issues/3195
  if (include && include.length && include[0].relation === 'todoList') {
    result.todoList = await this.todoList(result.id);
  }

  return result;
}

We’re now ready to expose TodoList and its related Todo API through the controller.

Previous step: Add TodoList model

Last step: Add TodoList controller