LoopBack 4 TodoList Application Tutorial - Add TodoList Repository

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

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

Create your repository

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

  1. lb4 repository
  2. ? Please select the datasource DbDatasource
  3. ? Select the model(s) you want to generate a repository TodoList
  4. create src/repositories/todo-list.repository.ts
  5. update src/repositories/index.ts
  6. ? Please select the repository base class DefaultCrudRepository (Legacy juggler
  7. bridge)
  8. 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 constrainedTodoRepository
  • inject TodoRepository instanceOnce the property type for todos has been defined, usethis.createHasManyRepositoryFactoryFor to assign it a repository constrainingfactory function. Pass in the name of the relationship (todos) and the Todorepository instance to constrain as the arguments for the function.

src/repositories/todo-list.repository.ts

  1. import {Getter, inject} from '@loopback/core';
  2. import {
  3. DefaultCrudRepository,
  4. HasManyRepositoryFactory,
  5. juggler,
  6. repository,
  7. } from '@loopback/repository';
  8. import {Todo, TodoList, TodoListRelations} from '../models';
  9. import {TodoRepository} from './todo.repository';
  10. export class TodoListRepository extends DefaultCrudRepository<
  11. TodoList,
  12. typeof TodoList.prototype.id,
  13. TodoListRelations
  14. > {
  15. public readonly todos: HasManyRepositoryFactory<
  16. Todo,
  17. typeof TodoList.prototype.id
  18. >;
  19. constructor(
  20. @inject('datasources.db') dataSource: juggler.DataSource,
  21. @repository.getter(TodoRepository)
  22. protected todoRepositoryGetter: Getter<TodoRepository>,
  23. ) {
  24. super(TodoList, dataSource);
  25. this.todos = this.createHasManyRepositoryFactoryFor(
  26. 'todos',
  27. todoRepositoryGetter,
  28. );
  29. }
  30. }

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

First add the following imports:

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

Add the following two functions after the constructor:

src/repositories/todo-list.repository.ts

  1. async find(
  2. filter?: Filter<TodoList>,
  3. options?: Options,
  4. ): Promise<TodoListWithRelations[]> {
  5. // Prevent juggler for applying "include" filter
  6. // Juggler is not aware of LB4 relations
  7. const include = filter && filter.include;
  8. filter = {...filter, include: undefined};
  9. const result = await super.find(filter, options);
  10. // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  11. // and use `inq` operator to fetch related todos in fewer DB queries
  12. // this is a temporary implementation, please see
  13. // https://github.com/strongloop/loopback-next/issues/3195
  14. if (include && include.length && include[0].relation === 'todos') {
  15. await Promise.all(
  16. result.map(async r => {
  17. r.todos = await this.todos(r.id).find();
  18. }),
  19. );
  20. }
  21. return result;
  22. }
  23. async findById(
  24. id: typeof TodoList.prototype.id,
  25. filter?: Filter<TodoList>,
  26. options?: Options,
  27. ): Promise<TodoListWithRelations> {
  28. // Prevent juggler for applying "include" filter
  29. // Juggler is not aware of LB4 relations
  30. const include = filter && filter.include;
  31. filter = {...filter, include: undefined};
  32. const result = await super.findById(id, filter, options);
  33. // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  34. // and use `inq` operator to fetch related todos in fewer DB queries
  35. // this is a temporary implementation, please see
  36. // https://github.com/strongloop/loopback-next/issues/3195
  37. if (include && include.length && include[0].relation === 'todos') {
  38. result.todos = await this.todos(result.id).find();
  39. }
  40. return result;
  41. }

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

  1. {
  2. "id": 2,
  3. "title": "My daily chores",
  4. "todos": [
  5. {
  6. "id": 3,
  7. "title": "play space invaders",
  8. "desc": "Become the very best!",
  9. "todoListId": 2
  10. }
  11. ]
  12. }

Let’s do the same on the TodoRepository:

src/repositories/todo.repository.ts

  1. async find(
  2. filter?: Filter<Todo>,
  3. options?: Options,
  4. ): Promise<TodoWithRelations[]> {
  5. // Prevent juggler for applying "include" filter
  6. // Juggler is not aware of LB4 relations
  7. const include = filter && filter.include;
  8. filter = {...filter, include: undefined};
  9. const result = await super.find(filter, options);
  10. // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  11. // and use `inq` operator to fetch related todo-lists in fewer DB queries
  12. // this is a temporary implementation, please see
  13. // https://github.com/strongloop/loopback-next/issues/3195
  14. if (include && include.length && include[0].relation === 'todoList') {
  15. await Promise.all(
  16. result.map(async r => {
  17. r.todoList = await this.todoList(r.id);
  18. }),
  19. );
  20. }
  21. return result;
  22. }
  23. async findById(
  24. id: typeof Todo.prototype.id,
  25. filter?: Filter<Todo>,
  26. options?: Options,
  27. ): Promise<TodoWithRelations> {
  28. // Prevent juggler for applying "include" filter
  29. // Juggler is not aware of LB4 relations
  30. const include = filter && filter.include;
  31. filter = {...filter, include: undefined};
  32. const result = await super.findById(id, filter, options);
  33. // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo
  34. // and use `inq` operator to fetch related todo-lists in fewer DB queries
  35. // this is a temporary implementation, please see
  36. // https://github.com/strongloop/loopback-next/issues/3195
  37. if (include && include.length && include[0].relation === 'todoList') {
  38. result.todoList = await this.todoList(result.id);
  39. }
  40. return result;
  41. }

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

Navigation

Previous step: Add TodoList model

Last step: Add TodoList controller