Skip to content

Commit

Permalink
docs: clean up todo tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
dhmlau committed Oct 17, 2018
1 parent b346d8c commit c58b1b0
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 240 deletions.
145 changes: 20 additions & 125 deletions docs/site/todo-tutorial-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,147 +26,42 @@ For more information about Controllers, see

### Create your controller

So, let's create a controller to handle our Todo routes. You can create an empty
Controller using the CLI as follows:
You can create a REST controller using the CLI as follows:

```sh
lb4 controller
? Controller class name: todo
? What kind of controller would you like to generate? Empty Controller
? 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? Todo
? What is the name of your CRUD repository? TodoRepository
? What is the type of your ID? number
? What is the base HTTP path name of the CRUD operations? /todos
create src/controllers/todo.controller.ts
update src/controllers/index.ts

Controller todo was created in src/controllers/
```

In addition to creating the handler functions themselves, we'll also be adding
decorators that setup the routing as well as the expected parameters of incoming
requests.

First, we need to define our basic controller class as well as plug in our
repository, which we'll use to perform our operations against the datasource.

#### src/controllers/todo.controller.ts

```ts
import {repository} from '@loopback/repository';
import {TodoRepository} from '../repositories/todo.repository';

export class TodoController {
constructor(@repository(TodoRepository) protected todoRepo: TodoRepository) {}
}
```

The `@repository` decorator will retrieve and inject an instance of the
`TodoRepository` whenever an inbound request is being handled. The lifecycle of
controller objects is per-request, which means that a new controller instance is
created for each request. As a result, we want to inject our `TodoRepository`
since the creation of these instances is more complex and expensive than making
new controller instances.
Let's review the `TodoController` located in
`src/controllers/todo.controller.ts`. The `@repository` decorator will retrieve
and inject an instance of the `TodoRepository` whenever an inbound request is
being handled. The lifecycle of controller objects is per-request, which means
that a new controller instance is created for each request. As a result, we want
to inject our `TodoRepository` since the creation of these instances is more
complex and expensive than making new controller instances.

> **NOTE**: You can customize the lifecycle of _all_ bindings in LoopBack 4!
> Controllers can easily be made to use singleton lifecycles to minimize startup
> costs. For more information, see the
> [Dependency injection](Dependency-injection.md) section of our docs.
Now that the repository is connected, let's create our first controller
function.

#### src/controllers/todo.controller.ts

```ts
import {repository} from '@loopback/repository';
import {TodoRepository} from '../repositories';
import {Todo} from '../models';
import {HttpErrors, post, param, requestBody} from '@loopback/rest';

export class TodoController {
constructor(@repository(TodoRepository) protected todoRepo: TodoRepository) {}

@post('/todos')
async createTodo(@requestBody() todo: Todo) {
if (!todo.title) {
throw new HttpErrors.BadRequest('title is required');
}
return await this.todoRepo.create(todo);
}
}
```

In this example, we're using two new decorators to provide LoopBack with
metadata about the route, verb and the format of the incoming request body:
In this example, there are two new decorators to provide LoopBack with metadata
about the route, verb and the format of the incoming request body:

- `@post('/todos')` creates metadata for `@loopback/rest` so that it can
redirect requests to this function when the path and verb match.
- `@requestBody()` associates the OpenAPI schema for a Todo with the body of the
request so that LoopBack can validate the format of an incoming request
(**Note**: As of this writing, schematic validation is not yet functional).

We've also added our own validation logic to ensure that a user will receive an
error if they fail to provide a `title` property with their `POST` request.

Lastly, we are using the functions provided by our `TodoRepository` instance to
perform a create operation against the datasource.

You can use these and other decorators to create a REST API for a full set of
verbs:

#### src/controllers/todo.controller.ts

```ts
import {repository} from '@loopback/repository';
import {TodoRepository} from '../repositories';
import {Todo} from '../models';
import {
HttpErrors,
post,
param,
requestBody,
get,
put,
patch,
del,
} from '@loopback/rest';

export class TodoController {
constructor(@repository(TodoRepository) protected todoRepo: TodoRepository) {}

@post('/todos')
async createTodo(@requestBody() todo: Todo) {
if (!todo.title) {
throw new HttpErrors.BadRequest('title is required');
}
return await this.todoRepo.create(todo);
}

@get('/todos/{id}')
async findTodoById(@param.path.number('id') id: number): Promise<Todo> {
return await this.todoRepo.findById(id);
}

@get('/todos')
async findTodos(): Promise<Todo[]> {
return await this.todoRepo.find();
}

@put('/todos/{id}')
async replaceTodo(
@param.path.number('id') id: number,
@requestBody() todo: Todo,
): Promise<void> {
return await this.todoRepo.replaceById(id, todo);
}

@patch('/todos/{id}')
async updateTodo(
@param.path.number('id') id: number,
@requestBody() todo: Todo,
): Promise<void> {
await this.todoRepo.updateById(id, todo);
}

@del('/todos/{id}')
async deleteTodo(@param.path.number('id') id: number): Promise<void> {
await this.todoRepo.deleteById(id);
}
}
```
request so that LoopBack can validate the format of an incoming request.

Some additional things to note about this example:

Expand Down
23 changes: 11 additions & 12 deletions docs/site/todo-tutorial-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ let you label tasks so that you can distinguish between them, add extra
information to describe those tasks, and finally, provide a way of tracking
whether or not they're complete.

For our Todo model to represent our Todo instances for our business domain, it
will need:
The to-do model has the following properties:

- a unique id
- a title
- a description that details what the todo is all about
- a boolean flag for whether or not we've completed the task
- `id`: a unique id
- `title`: a title
- `desc`: a description that details the specific task to be accomplished
- `isComplete`: a boolean flag for whether or not weve completed the task

We can use the `lb4 model` command and answer the prompts to generate the model
for us. Press `return` with an empty property name to generate the model. Follow
Expand All @@ -51,39 +50,39 @@ these steps:
```sh
lb4 model
? Model class name: todo
? Please select the model base class Entity
? Please select the model base class: Entity

Let's add a property to Todo
Enter an empty property name when done
? Enter the property name: id
? Property type: number
? Is ID field? Yes
? Required?: No
? Is id the ID property? Yes
? Is it required?: No
? Default value [leave blank for none]:
Let's add another property to Todo
Enter an empty property name when done

? Enter the property name: title
? Property type: string
? Required?: Yes
? Is it required?: Yes
? Default value [leave blank for none]:

Let's add another property to Todo
Enter an empty property name when done
? Enter the property name: desc
? Property type: string
? Required?: No
? Is it required?: No
? Default value [leave blank for none]:
Let's add another property to Todo
Enter an empty property name when done

? Enter the property name: isComplete
? Property type: boolean
? Required?: No
? Is it required?: No
? Default value [leave blank for none]:

Let's add another property to Todo
Expand Down
45 changes: 1 addition & 44 deletions docs/site/todo-tutorial-putting-it-together.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ summary: LoopBack 4 Todo Application Tutorial - Putting it all together

### Putting it all together

We've got all of our artifacts now, and all that's left is to bind them to our
We've got all of our artifacts now, and they are all automatically bound to our
[Application](Application.md) so that LoopBack's
[Dependency injection](Dependency-injection.md) system can tie it all together
for us!
Expand All @@ -31,49 +31,6 @@ artifacts and inject them into our application for use.
> [Booters](Booting-an-Application.md#booters) section of
> [Booting an Application](Booting-an-Application.md).
#### src/application.ts

```ts
import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {RepositoryMixin} from '@loopback/repository';
import {RestApplication, RestServer} from '@loopback/rest';
import {MySequence} from './sequence';

export class TodoListApplication extends BootMixin(
RepositoryMixin(RestApplication),
) {
constructor(options?: ApplicationConfig) {
super(options);

// Set up the custom sequence
this.sequence(MySequence);

this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
controllers: {
// Customize ControllerBooter Conventions here
dirs: ['controllers'],
extensions: ['.controller.js'],
nested: true,
},
};
}

async start() {
await super.start();

const server = await this.getServer(RestServer);
const port = await server.get<number>('rest.port');
console.log(`Server is running at http://127.0.0.1:${port}`);
console.log(`Try http://127.0.0.1:${port}/ping`);
}
}
```

### Try it out

Let's try out our application! First, you'll want to start the app.

```sh
Expand Down
78 changes: 20 additions & 58 deletions docs/site/todo-tutorial-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,67 +24,29 @@ For more information about Repositories, see

### Create your repository

In the `src/repositories` directory, create two files:

- `index.ts` (our export helper)
- `todo.repository.ts`

> **NOTE:** The `index.ts` file is an export helper file; this pattern is a huge
> time-saver as the number of models in your project grows, because it allows
> you to point to the _directory_ when attempting to import types from a file
> within the target folder. **We will use this concept throughout the tutorial!
> For more info, see TypeScript's
> [Module Resolution](https://www.typescriptlang.org/docs/handbook/module-resolution.html)
> docs.**
```ts
// in src/models/index.ts
export * from './foo.model';
export * from './bar.model';
export * from './baz.model';

// elsewhere...

// with index.ts
import {Foo, Bar, Baz} from './models';
// ...and without index.ts
import {Foo} from './models/foo.model';
import {Bar} from './models/bar.model';
import {Baz} from './models/baz.model';
// Using an index.ts in your artifact folders really helps keep
// things tidy and succinct!
From inside the project folder, run the `lb4 repository` command to create a
repository for your to-do model using the `db` datasource from the previous
step. The `db` datasource shows up by its class name `DbDataSource` from the
list of available datasources.

```sh
lb4 repository
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a repository Todo
create src/repositories/todo.repository.ts
update src/repositories/index.ts

Repository Todo was created in src/repositories/
```
Our TodoRepository will extend a small base class that uses the
`DefaultCrudRepository` class from
[`@loopback/repository`](https://github.com/strongloop/loopback-next/tree/master/packages/repository)
and will define the model type we're working with, as well as its ID type. This
automatically gives us the basic CRUD methods required for performing operations
against our database (or any other kind of datasource).

We'll also inject our datasource so that this repository can connect to it when
executing data operations.

#### src/repositories/todo.repository.ts

```ts
import {DefaultCrudRepository, juggler} from '@loopback/repository';
import {Todo} from '../models';
import {inject} from '@loopback/core';

export class TodoRepository extends DefaultCrudRepository<
Todo,
typeof Todo.prototype.id
> {
constructor(@inject('datasources.db') dataSource: juggler.DataSource) {
super(Todo, dataSource);
}
}
```
The `src/repositories/index.ts` file makes exporting artifacts central and also
easier to import.
Now we have everything we need to perform CRUD operations for our Todo list,
we'll need to build the [Controller](todo-tutorial-controller.md) to handle our
incoming requests.
The newly created `todo.repository.ts` class has the necessary connections that
are needed to perform CRUD operations for our to-do model. It leverages the Todo
model definition and 'db' datasource configuration and retrieves the datasource
using
[Dependency Injection](https://loopback.io/doc/en/lb4/Dependency-injection.html).
### Navigation
Expand Down
3 changes: 2 additions & 1 deletion examples/todo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ This is the basic tutorial for getting started with Loopback 4!
## Overview

This tutorial demonstrates how to create a basic API for a todo list using
LoopBack 4.
LoopBack 4. You will experience how you can create REST APIs with just
[5 steps](#steps).

![todo-tutorial-overview](https://loopback.io/pages/en/lb4/imgs/todo-overview.png)

Expand Down

0 comments on commit c58b1b0

Please sign in to comment.