Page Contents

Defining business logic to handle requests to related models isn’t too different from handling requests for standalone models. We’ll create controllers to handle requests 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 TodoList routes with the following inputs:

$ lb4 controller
? Controller class name: TodoList
Controller TodoList will be created in src/controllers/todo-list.controller.ts

? What kind of controller would you like to generate? REST Controller with CRUD functions
? What is the name of the model to use with this CRUD repository? TodoList
? What is the name of your CRUD repository? TodoListRepository
? What is the name of ID property? id
? What is the type of your ID? number
? Is the id omitted when creating a new instance? Yes
? What is the base HTTP path name of the CRUD operations? /todo-lists
   create src/controllers/todo-list.controller.ts
   update src/controllers/index.ts

Controller TodoList was created in src/controllers/

To view the completed file, see the TodoList example.

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 the schema.

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

Then update the following schemas in responses’s content:

src/controllers/todo-list.controller.ts

@get('/todo-lists', {
  responses: {
    '200': {
      description: 'Array of TodoList model instances',
      content: {
        'application/json': {
          schema: {
            type: 'array',
            items: getModelSchemaRef(TodoList, {includeRelations: true}),
          },
        },
      },
    },
  },
})
async find(/*...*/) {/*...*/}

@get('/todo-lists/{id}', {
  responses: {
    '200': {
      description: 'TodoList model instance',
      content: {
        'application/json': {
          schema: getModelSchemaRef(TodoList, {includeRelations: true}),
        },
      },
    },
  },
})
async findById(/*...*/) {/*...*/}

Let’s also update it in the TodoController:

src/controllers/todo.controller.ts

@get('/todos', {
  responses: {
    '200': {
      description: 'Array of Todo model instances',
      content: {
        'application/json': {
          schema: {
            type: 'array',
            items: getModelSchemaRef(Todo, {includeRelations: true}),
          },
        },
      },
    },
  },
})
async findTodos(/*...*/) {/*...*/}

@get('/todos/{id}', {
  responses: {
    '200': {
      description: 'Todo model instance',
      content: {
        'application/json': {
          schema: getModelSchemaRef(Todo, {includeRelations: true}),
        },
      },
    },
  },
})
async findTodoById(/*...*/) {/*...*/}

Relation Controllers

Earlier when we used lb4 relation to create the two relations between Todo and TodoList, you may have noticed src/controllers/todo-todo-list.controller.ts and src/controllers/todo-list-todo.controller.ts were created. These files contain a set of API for the relations.

Relation controllers act in a similar manner to normal controllers, except they modify the relational property. For example, in the src/controllers/todo-list-todo.controller.ts file, we can do requests to the endpoint /todo-lists/{id}/todos, which we’ll see in the Try it out section.

As src/controllers/todo-todo-list.controller.ts only contains one method, we can move it to the Todo controller and delete that file:

src/models/todo.controller.ts

export class TodoController {
  constructor() {} // ...

  // other controller methods

  @get('/todos/{id}/todo-list', {
    responses: {
      '200': {
        description: 'TodoList belonging to Todo',
        content: {
          'application/json': {
            schema: {type: 'array', items: getModelSchemaRef(TodoList)},
          },
        },
      },
    },
  })
  async getTodoList(
    @param.path.number('id') id: typeof Todo.prototype.id,
  ): Promise<TodoList> {
    return this.todoRepository.todoList(id);
  }
}

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.

$ npm start
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 previous POST request and this body: { "title": "get eggs", "isComplete": false}. Notice that response body you get back contains property todoListId with the ID from before.
  • GET /todo-lists/{id}/todos with the ID from before, and see if you get the todo you created from before.
  • GET /todo-lists/{id} with the ID from before, with the following filter {include: [{relation: 'todos'}]}, and see if you get a todos property with the todo created before. Note: this filter won’t work through the API explorer (See this GitHub issue for details). Use the following url to test this endpoint (remember to replace {id} with the ID from before): http://localhost:3000/todo-lists/{id}?filter[include][][relation]=todos

And there you have it! You now have the power to define APIs for related models!

Previous step: Add Model Relations