LoopBack 4 TodoList Application Tutorial - Add TodoList and TodoList's Todo Controller

Defining business logic to handle requests to related models isn’t too differentfrom handling requests for standalone models. We’ll create controllers to handlerequests for todo-lists and todo items under a todo-list.

Create TodoList controller

Run the CLI command for creating a RESTful CRUD controller for our TodoListroutes with the following inputs:

  1. $ lb4 controller
  2. ? Controller class name: TodoList
  3. Controller TodoList will be created in src/controllers/todo-list.controller.ts
  4. ? What kind of controller would you like to generate? REST Controller with CRUD functions
  5. ? What is the name of the model to use with this CRUD repository? TodoList
  6. ? What is the name of your CRUD repository? TodoListRepository
  7. ? What is the name of ID property? id
  8. ? What is the type of your ID? number
  9. ? What is the base HTTP path name of the CRUD operations? /todo-lists
  10. create src/controllers/todo-list.controller.ts
  11. update src/controllers/index.ts
  12. Controller TodoList was created in src/controllers/

And voilà! We now have a set of basic APIs for todo-lists, just like that!

In order to get our related Todos for each TodoList, let’s update theschema.

In src/models/todo-list.controller.ts, first import getModelSchemaRef from@loopback/rest.

Then update the following schemas in responses’s content:

src/models/todo-list.controller.ts

  1. @get('/todo-lists', {
  2. responses: {
  3. '200': {
  4. description: 'Array of TodoList model instances',
  5. content: {
  6. 'application/json': {
  7. schema: {
  8. type: 'array',
  9. items: getModelSchemaRef(TodoList, {includeRelations: true}),
  10. },
  11. },
  12. },
  13. },
  14. },
  15. })
  16. async find(/*...*/) {/*...*/}
  17. @get('/todo-lists/{id}', {
  18. responses: {
  19. '200': {
  20. description: 'TodoList model instance',
  21. content: {
  22. 'application/json': {
  23. schema: getModelSchemaRef(TodoList, {includeRelations: true}),
  24. },
  25. },
  26. },
  27. },
  28. })
  29. async findById(/*...*/) {/*...*/}

Let’s also update it in the TodoController:

src/models/todo.controller.ts

  1. @get('/todos', {
  2. responses: {
  3. '200': {
  4. description: 'Array of Todo model instances',
  5. content: {
  6. 'application/json': {
  7. schema: {
  8. type: 'array',
  9. items: getModelSchemaRef(Todo, {includeRelations: true}),
  10. },
  11. },
  12. },
  13. },
  14. },
  15. })
  16. })
  17. async findTodos(/*...*/) {/*...*/}
  18. @get('/todos/{id}', {
  19. responses: {
  20. '200': {
  21. description: 'Todo model instance',
  22. content: {
  23. 'application/json': {
  24. schema: getModelSchemaRef(Todo, {includeRelations: true}),
  25. },
  26. },
  27. },
  28. },
  29. })
  30. async findTodoById(/*...*/) {/*...*/}

Create TodoList’s Todo controller

For the controller handling Todos of a TodoList, we’ll start with an emptycontroller:

  1. $ lb4 controller
  2. ? Controller class name: TodoListTodo
  3. Controller TodoListTodo will be created in src/controllers/todo-list-todo.controller.ts
  4. ? What kind of controller would you like to generate? Empty Controller
  5. create src/controllers/todo-list-todo.controller.ts
  6. update src/controllers/index.ts
  7. Controller TodoListTodo was created in src/controllers/

Let’s add in an injection for our TodoListRepository:

src/controllers/todo-list-todo.controller.ts

  1. import {repository} from '@loopback/repository';
  2. import {TodoListRepository} from '../repositories';
  3. export class TodoListTodoController {
  4. constructor(
  5. @repository(TodoListRepository) protected todoListRepo: TodoListRepository,
  6. ) {}
  7. }

We’re now ready to add in some routes for our todo requests. To call the CRUDmethods on a todo-list’s todo items, we’ll first need to create a constrainedTodoRepository. We can achieve this by using our repository instance’s todosfactory function that we defined earlier in TodoListRepository.

The POST request from /todo-lists/{id}/todos should look similar to thefollowing request:

src/controllers/todo-list-todo.controller.ts

  1. import {repository} from '@loopback/repository';
  2. import {TodoListRepository} from '../repositories';
  3. import {post, param, requestBody} from '@loopback/rest';
  4. import {Todo} from '../models';
  5. export class TodoListTodoController {
  6. constructor(
  7. @repository(TodoListRepository) protected todoListRepo: TodoListRepository,
  8. ) {}
  9. @post('/todo-lists/{id}/todos')
  10. async create(
  11. @param.path.number('id') id: number,
  12. @requestBody({
  13. content: {
  14. 'application/json': {
  15. schema: getModelSchemaRef(Todo, {exclude: ['id']}),
  16. },
  17. },
  18. })
  19. todo: Omit<Todo, 'id'>,
  20. ) {
  21. return this.todoListRepo.todos(id).create(todo);
  22. }
  23. }

Using our constraining factory as we did with the POST request, we’ll definethe controller methods for the rest of the HTTP verbs for the route. Thecompleted controller should look as follows:

src/controllers/todo-list-todo.controller.ts

  1. import {
  2. Count,
  3. CountSchema,
  4. Filter,
  5. repository,
  6. Where,
  7. } from '@loopback/repository';
  8. import {
  9. del,
  10. get,
  11. getModelSchemaRef,
  12. getWhereSchemaFor,
  13. param,
  14. patch,
  15. post,
  16. requestBody,
  17. } from '@loopback/rest';
  18. import {Todo} from '../models';
  19. import {TodoListRepository} from '../repositories';
  20. export class TodoListTodoController {
  21. constructor(
  22. @repository(TodoListRepository) protected todoListRepo: TodoListRepository,
  23. ) {}
  24. @post('/todo-lists/{id}/todos', {
  25. responses: {
  26. '200': {
  27. description: 'TodoList.Todo model instance',
  28. content: {'application/json': {schema: getModelSchemaRef(Todo)}},
  29. },
  30. },
  31. })
  32. async create(
  33. @param.path.number('id') id: number,
  34. @requestBody({
  35. content: {
  36. 'application/json': {
  37. schema: getModelSchemaRef(Todo, {exclude: ['id']}),
  38. },
  39. },
  40. })
  41. todo: Omit<Todo, 'id'>,
  42. ): Promise<Todo> {
  43. return this.todoListRepo.todos(id).create(todo);
  44. }
  45. @get('/todo-lists/{id}/todos', {
  46. responses: {
  47. '200': {
  48. description: "Array of Todo's belonging to TodoList",
  49. content: {
  50. 'application/json': {
  51. schema: {type: 'array', items: getModelSchemaRef(Todo)},
  52. },
  53. },
  54. },
  55. },
  56. })
  57. async find(
  58. @param.path.number('id') id: number,
  59. @param.query.object('filter') filter?: Filter<Todo>,
  60. ): Promise<Todo[]> {
  61. return this.todoListRepo.todos(id).find(filter);
  62. }
  63. @patch('/todo-lists/{id}/todos', {
  64. responses: {
  65. '200': {
  66. description: 'TodoList.Todo PATCH success count',
  67. content: {'application/json': {schema: CountSchema}},
  68. },
  69. },
  70. })
  71. async patch(
  72. @param.path.number('id') id: number,
  73. @requestBody({
  74. content: {
  75. 'application/json': {
  76. schema: getModelSchemaRef(Todo, {partial: true}),
  77. },
  78. },
  79. })
  80. todo: Partial<Todo>
  81. @param.query.object('where', getWhereSchemaFor(Todo)) where?: Where<Todo>,
  82. ): Promise<Count> {
  83. return this.todoListRepo.todos(id).patch(todo, where);
  84. }
  85. @del('/todo-lists/{id}/todos', {
  86. responses: {
  87. '200': {
  88. description: 'TodoList.Todo DELETE success count',
  89. content: {'application/json': {schema: CountSchema}},
  90. },
  91. },
  92. })
  93. async delete(
  94. @param.path.number('id') id: number,
  95. @param.query.object('where', getWhereSchemaFor(Todo)) where?: Where<Todo>,
  96. ): Promise<Count> {
  97. return this.todoListRepo.todos(id).delete(where);
  98. }
  99. }

Check out our TodoList example to see the full source code generated for theTodoListTodo controller:src/controllers/todo-list-todo.controller.ts

Try it out

With the controllers complete, your application is ready to start up again!@loopback/boot should wire up everything for us when we start the application,so there’s nothing else we need to do before we try out our new routes.

  1. $ npm start
  2. Server is running at http://127.0.0.1:3000

Here are some new requests you can try out:

  • POST /todo-lists with a body of { "title": "grocery list" }.
  • POST /todo-lists/{id}/todos using the ID you got back from the previousPOST request and a body for a todo. Notice that response body you get backcontains property todoListId with the ID from before.
  • GET /todo-lists/{id}/todos and see if you get the todo you created frombefore.And there you have it! You now have the power to define APIs for related models!

Navigation

Previous step: Add TodoList repository