From bb86f04206158d356384a8dd0de98b599fd55902 Mon Sep 17 00:00:00 2001 From: Kevin Delisle Date: Thu, 1 Feb 2018 17:16:40 -0500 Subject: [PATCH] docs(lb4): reorganize current tutorial materials This is a rework of the existing tutorial materials and layout. - Reorganize the current content under the "Examples and tutorials" section - Migrate all of the existing tutorials underneath this section - More clearly separate the "hello world" and "getting started" tutorials by renaming them and linking accordingly - Make use of the existing getting-started readme for the garden path tutorial --- _data/sidebars/lb4_sidebar.yml | 12 +- pages/en/lb4/Examples-and-tutorials.md | 13 +- pages/en/lb4/Getting-started.md | 92 +--- pages/en/lb4/Hello-World.md | 99 +++++ pages/en/lb4/includes/lb4_install.md | 2 +- .../example-getting-started/README.md | 401 ++++++++++++++++++ 6 files changed, 523 insertions(+), 96 deletions(-) create mode 100644 pages/en/lb4/Hello-World.md create mode 100644 pages/en/lb4/readmes/loopback-next/packages/example-getting-started/README.md diff --git a/_data/sidebars/lb4_sidebar.yml b/_data/sidebars/lb4_sidebar.yml index 069b4f11b..7da2024a5 100644 --- a/_data/sidebars/lb4_sidebar.yml +++ b/_data/sidebars/lb4_sidebar.yml @@ -15,15 +15,19 @@ children: url: Installation.html output: 'web, pdf' -- title: 'Getting started' - url: Getting-started.html +- title: 'Examples and tutorials' + url: Examples-and-tutorials.html output: 'web, pdf' children: - - title: 'Examples and tutorials' - url: Examples-and-tutorials.html + - title: 'Hello World!' + url: Hello-World.html output: 'web, pdf' + - title: 'Getting Started' + url: Getting-started.html + output: 'web, pdf' + - title: 'Key concepts' url: Concepts.html output: 'web, pdf' diff --git a/pages/en/lb4/Examples-and-tutorials.md b/pages/en/lb4/Examples-and-tutorials.md index ab4594474..b7e9424db 100644 --- a/pages/en/lb4/Examples-and-tutorials.md +++ b/pages/en/lb4/Examples-and-tutorials.md @@ -8,13 +8,15 @@ permalink: /doc/en/lb4/Examples-and-tutorials.html summary: --- -LoopBack 4 comes with the following example projects: +Here's a list of tutorial resources to help you jump into LoopBack 4! -- **[hello-world](https://github.com/strongloop/loopback-next/tree/master/packages/example-hello-world)**: - Tutorial on setting up a simple hello-world application using LoopBack 4. +* **[Hello World](Hello-World.html)**: + Tutorial on setting up a simple hello-world application using LoopBack 4 with + a REST server and a controller. -- **[getting-started](https://github.com/strongloop/loopback-next/tree/master/packages/example-getting-started)**: - Tutorial on building a simple application with LoopBack 4 key concepts. +* **[Getting Started](Getting-started.html)**: + Tutorial on building a Todo API that leverages key LoopBack 4 concepts like + controllers, sequences, repositories, datasources, context and more! - **[log-extension](https://github.com/strongloop/loopback-next/tree/master/packages/example-log-extension)**: Tutorial on building a log extension. @@ -24,7 +26,6 @@ LoopBack 4 comes with the following example projects: You can download any of the example projects usig our CLI tool `lb4`: - ``` $ lb4 example ? What example would you like to clone? (Use arrow keys) diff --git a/pages/en/lb4/Getting-started.md b/pages/en/lb4/Getting-started.md index 39632d4ba..821f25de3 100644 --- a/pages/en/lb4/Getting-started.md +++ b/pages/en/lb4/Getting-started.md @@ -1,90 +1,12 @@ --- +title: "Getting Started" lang: en -title: 'Getting started' -keywords: LoopBack 4.0, LoopBack 4 -tags: +layout: readme +source: loopback-next +file: packages/example-getting-started/README.md +keywords: LoopBack, LoopBack 4 +tags: [tutorial, getting-started] sidebar: lb4_sidebar permalink: /doc/en/lb4/Getting-started.html -summary: Write and run a LoopBack 4 "Hello World" project in JavaScript and TypeScript. +summary: The introductory tutorial for learning LoopBack 4. --- -## Prerequisites - -Install [Node.js](https://nodejs.org/en/download/) (version 8.x.x or higher) if -not already installed on your machine. - -## Install LoopBack 4 CLI - -The LoopBack 4 CLI is a command-line interface that can scaffold a project or -extension with more features under development. CLI provides the fastest way to -get started with a LoopBack 4 project that adheres to best practices. - -Install the CLI globally by running -```sh -npm i -g @loopback/cli -``` - -## Create a new project - -The CLI tool will scaffold the project, configure TypeScript compiler and -install all the required dependencies. To create a new project, run the CLI as -follows and answer the prompts. -```sh -lb4 app -``` - -Answer the prompts as follows: -```sh -? Project name: getting-started -? Project description: Getting started tutorial -? Project root directory: (getting-started) -? Application class name: StarterApplication -? Select project build settings: Enable tslint, Enable prettier, Enable mocha, Enable loopbackBuild -``` - -### Starting the project - -The project comes with a "ping" route to test the project. Let's try it out by running the project. -```sh -cd getting-started -npm start -``` - -In a browser, visit [http://127.0.0.1:3000/ping](http://127.0.0.1:3000/ping). - -## Adding your own controller - -Now that we have a basic project created, it's time to add our own [controller](Controllers.html). -Let's add a simple "Hello World" controller as follows: - -* Create a new file in `/src/controllers/` called `hello.controller.ts`. - -* Paste the following contents into the file: - ```ts - import {get} from '@loopback/rest'; - - export class HelloController { - @get('/hello') - hello(): string { - return 'Hello world!'; - } - } - ``` - -* Update `/src/application.ts` to load the controller: - * Import `HelloController` at the top of the file - ```ts - import {HelloController} from './controllers/hello.controller'; - ``` - - * Add controller in `setupControllers()` - ```ts - setupControllers() { - this.controller(PingController); - this.controller(HelloController); - } - ``` - -* Start the application using `npm start`. - * *Note: If your application is still running, press **CTRL+C** to stop it before restarting it* - -* Visit [http://127.0.0.1:3000/hello](http://127.0.0.1:3000/hello) to see `Hello world!` diff --git a/pages/en/lb4/Hello-World.md b/pages/en/lb4/Hello-World.md new file mode 100644 index 000000000..f55e65051 --- /dev/null +++ b/pages/en/lb4/Hello-World.md @@ -0,0 +1,99 @@ +--- +lang: en +title: 'Hello World!' +keywords: LoopBack 4.0, LoopBack 4 +tags: +sidebar: lb4_sidebar +permalink: /doc/en/lb4/Hello-World.html +summary: Write and run a LoopBack 4 "Hello World" project in JavaScript and TypeScript. +--- + +## Prerequisites + +Install [Node.js](https://nodejs.org/en/download/) (version 8.x.x or higher) if +not already installed on your machine. + +## Install LoopBack 4 CLI + +The LoopBack 4 CLI is a command-line interface that can scaffold a project or +extension with more features under development. CLI provides the fastest way to +get started with a LoopBack 4 project that adheres to best practices. + +Install the CLI globally by running + +```sh +npm i -g @loopback/cli +``` + +## Create a new project + +The CLI tool will scaffold the project, configure TypeScript compiler and +install all the required dependencies. To create a new project, run the CLI as +follows and answer the prompts. + +```sh +lb4 app +``` + +Answer the prompts as follows: + +```sh +? Project name: hello-world +? Project description: A Hello World app! +? Project root directory: (hello-world) +? Application class name: StarterApplication +? Select project build settings: Enable tslint, Enable prettier, Enable mocha, Enable loopbackBuild +``` + +### Starting the project + +The project comes with a "ping" route to test the project. Let's try it out by running the project. + +```sh +cd hello-world +npm start +``` + +In a browser, visit [http://127.0.0.1:3000/ping](http://127.0.0.1:3000/ping). + +## Adding your own controller + +Now that we have a basic project created, it's time to add our own [controller](Controllers.html). +Let's add a simple "Hello World" controller as follows: + +* Create a new file in `/src/controllers/` called `hello.controller.ts`. + +* Paste the following contents into the file: + + ```ts + import { get } from '@loopback/rest'; + + export class HelloController { + @get('/hello') + hello(): string { + return 'Hello world!'; + } + } + ``` + +* Update `/src/application.ts` to load the controller: + + * Import `HelloController` at the top of the file + + ```ts + import { HelloController } from './controllers/hello.controller'; + ``` + + * Add controller in `setupControllers()` + ```ts + setupControllers() { + this.controller(PingController); + this.controller(HelloController); + } + ``` + +* Start the application using `npm start`. + + * _Note: If your application is still running, press **CTRL+C** to stop it before restarting it_ + +* Visit [http://127.0.0.1:3000/hello](http://127.0.0.1:3000/hello) to see `Hello world!` diff --git a/pages/en/lb4/includes/lb4_install.md b/pages/en/lb4/includes/lb4_install.md index 522228850..27c526f3d 100644 --- a/pages/en/lb4/includes/lb4_install.md +++ b/pages/en/lb4/includes/lb4_install.md @@ -15,7 +15,7 @@ Install it with the following command: $ npm i --save @loopback/core ``` -{% include tip.html content="The `@loopback/core` package is the bare minimum; depending on your project's requirements, you may need to install other LoopBack packages. See [Getting started](Getting-started.html) for an example. +{% include tip.html content="The `@loopback/core` package is the bare minimum; depending on your project's requirements, you may need to install other LoopBack packages. See the [Tutorial](Tutorial.html) for an example. " %} Now `package.json` should include these dependencies (you may see different version numbers): diff --git a/pages/en/lb4/readmes/loopback-next/packages/example-getting-started/README.md b/pages/en/lb4/readmes/loopback-next/packages/example-getting-started/README.md new file mode 100644 index 000000000..29c922893 --- /dev/null +++ b/pages/en/lb4/readmes/loopback-next/packages/example-getting-started/README.md @@ -0,0 +1,401 @@ +# @loopback/example-getting-started + +This is the basic tutorial for getting started with Loopback 4! + +**NOTICE**: This tutorial is currently under construction! This notice will be +removed when it is ready for use! + +## Prerequisites + +Before we can begin, you'll need to make sure you have some things installed: +- [Node.js](https://nodejs.org/en/) at v6.x or greater + +Additionally, this tutorial assumes that you are comfortable with +certain technologies, languages and concepts. +- JavaScript (ES6) +- [npm](https://www.npmjs.com/) +- [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) + +## Setup +1. Install the new loopback CLI toolkit. +``` +npm i -g @loopback/cli +``` +2. Download the "getting-started" application. +``` +lb4 example getting-started +``` + +3. Switch to the directory and install dependencies. +``` +cd loopback-example-getting-started && npm i +``` + +4. Start the app! +``` +npm start +``` + +## Tutorial + +Here's a step-by-step guide of how to build this repository! + +### Create your app scaffolding +Install the `@loopback/cli` package. This will give you the command-line +toolkit that can generate a basic REST app for you. +`npm i -g @loopback/cli` + +Next, navigate to whichever directory you'd like to create your new project +and run `lb4`. Follow the prompts to generate your application. For this +tutorial, when prompted with the options for selecting things like whether or +not to enable certain project features (loopback's build, tslint, mocha, etc.), +leave them all enabled. + + + +### Adding Legacy Juggler Capabilities +Jump into the directory for your new application. You'll see a folder structure +similar to this: +``` +dist\ +node_modules\ +src\ + controllers\ + ping.controller.ts + README.md + repositories\ + README.md + application.ts + index.ts +test\ + mocha.opts + ping.controller.test.ts + README.md +index.js +index.d.ts +index.ts +``` + +The application template comes with a controller, and some default wireup in +`src/application.ts` that handles the basic configuration for your application. +For this tutorial, we won't need `ping.controller.ts` or its corresponding test, +but you can leave them in for now. + +Now that you have your setup, it's time to modify it to add in +`@loopback/repository`. Install this dependency by running +`npm i --save @loopback/repository`. + +Next, modify `src/application.ts` to change the base class of your app to use +the `RepositoryMixin`: + +#### src/application.ts +```ts +import {ApplicationConfig} from '@loopback/core'; +import {RestApplication} from '@loopback/rest'; +import {PingController} from './controllers/ping-controller'; +import {Class, Repository, RepositoryMixin} from '@loopback/repository'; + +export class TodoApplication extends RepositoryMixin(RestApplication) { + constructor(options?: ApplicationConfig) { + super(options); + this.setupControllers(); + } + + setupControllers() { + this.controller(PingController); + } +} +``` + +### Building the Todo model +The Todo model will be the object we use both as a Data Transfer Object (DTO) on +the controller, and as a LoopBack model for the Legacy Juggler implementation. + +Create another folder in `src` called `repositories` and inside of that folder, +create two files: +- `index.ts` +- `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! +```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'; +``` + +In our Todo model, we'll create a basic representation of what would go in +a Todo list. Our model will include: +- 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. + +For the Legacy Juggler to understand how to work with our model class, it +will need to extend the `Entity` type, as well as provide an override for +the `getId` function, so that it can retrieve a Todo model's ID as needed. + +Additionally, we'll define a `SchemaObject` that represents our Todo model +as an [OpenAPI Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schema-object). +This will give the OpenAPI spec builder the information it needs to describe the +Todo model on your app's OpenAPI endpoints. + +#### src/models/todo.model.ts +```ts +import {Entity, property, model} from '@loopback/repository'; +import {SchemaObject} from '@loopback/openapi-spec'; + +@model() +export class Todo extends Entity { + @property({ + type: 'number', + id: true + }) + id?: number; + + @property({ + type: 'string', + required: true + }) + title: string; + + @property({ + type: 'string' + }) + desc?: string; + + @property({ + type: 'boolean' + }) + isComplete: boolean; + + getId() { + return this.id; + } +} + +export const TodoSchema: SchemaObject = { + title: 'todoItem', + properties: { + id: { + type: 'number', + description: 'ID number of the Todo entry.' + }, + title: { + type: 'string', + description: 'Title of the Todo entry.' + }, + desc: { + type: 'number', + description: 'ID number of the Todo entry.' + }, + isComplete: { + type: 'boolean', + description: 'Whether or not the Todo entry is complete.' + } + }, + required: ['title'], +}; +``` + +### Building a Datasource +Before we can begin constructing controllers and repositories for our +application, we need to define our datasource. + +Create a new folder in the root directory of the project called `config`, +and then inside that folder, create a `datasources.json` file. For now, we'll +be using the memory connector provided with the Juggler. + +#### config/datasources.json +```json +{ + "name": "ds", + "connector": "memory" +} +``` + +Create another folder called `datasources` in the `src` directory, and inside +that folder, create a new file called `db.datasource.ts`. + +#### src/datasources/db.datasource.ts + +```ts +import * as path from 'path'; +import * as fs from 'fs'; +import { DataSourceConstructor, juggler } from '@loopback/repository'; + +const dsConfigPath = path.resolve('config', 'datasources.json'); +const config = require(dsConfigPath); +export const db = new DataSourceConstructor(config); +``` + +This will give us a strongly-typed datasource export that we can work with to +construct our TodoRepository definition. + +### Create your repository +Create another folder in `src` called `repositories` and inside of that folder, +create two files: +- `index.ts` (our export helper) +- `todo.repository.ts` + +Our TodoRepository will contain a small base class that uses the +`DefaultCrudRepository` class from `@loopback/repository` and will define the +model type we're working with, as well as its ID type. 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, DataSourceType } from '@loopback/repository'; +import { Todo } from '../models'; +import { inject } from '@loopback/core'; + +export class TodoRepository extends DefaultCrudRepository< + Todo, + typeof Todo.prototype.id +> { + constructor(@inject('datasource') protected datasource: DataSourceType) { + super(Todo, datasource); + } +} +``` + + +### Create your controller +Now, we'll create a controller to handle our Todo routes. Create the +`src/controllers` directory and two files inside: +- `index.ts` (export helper) +- `todo.controller.ts` + +In addition to creating the CRUD methods themselves, we'll also be adding +decorators that setup the routing as well as the expected parameters of +incoming requests. + +#### src/controllers/todo.controller.ts +```ts +import {post, param, get, put, patch, del} from '@loopback/openapi-v2'; +import {HttpErrors} from '@loopback/rest'; +import {TodoSchema, Todo} from '../models'; +import {repository} from '@loopback/repository'; +import {TodoRepository} from '../repositories/index'; + +export class TodoController { + constructor( + @repository(TodoRepository.name) protected todoRepo: TodoRepository, + ) {} + @post('/todo') + @param.body('todo', TodoSchema) + async createTodo(todo: Todo) { + if (!todo.title) { + return Promise.reject(new HttpErrors.BadRequest('title is required')); + } + return await this.todoRepo.create(todo); + } + + @get('/todo/{id}') + @param.path.number('id') + @param.query.boolean('items') + async findTodoById(id: number, items?: boolean): Promise { + return await this.todoRepo.findById(id); + } + + @get('/todo') + async findTodos(): Promise { + return await this.todoRepo.find(); + } + + @put('/todo/{id}') + @param.path.number('id') + @param.body('todo', TodoSchema) + async replaceTodo(id: number, todo: Todo): Promise { + return await this.todoRepo.replaceById(id, todo); + } + + @patch('/todo/{id}') + @param.path.number('id') + @param.body('todo', TodoSchema) + async updateTodo(id: number, todo: Todo): Promise { + return await this.todoRepo.updateById(id, todo); + } + + @del('/todo/{id}') + @param.path.number('id') + async deleteTodo(id: number): Promise { + return await this.todoRepo.deleteById(id); + } +} +``` + +### Putting it all together + +Now that we've got all of our artifacts made, let's set them up in our +application! + +We'll define a new helper function for setting up the repositories, as well +as adding in our new controller binding. + +#### src/application.ts +```ts +import {ApplicationConfig} from '@loopback/core'; +import {RestApplication} from '@loopback/rest'; +import {TodoController, PingController} from './controllers'; +import { + Class, + Repository, + RepositoryMixin, + DataSourceConstructor, +} from '@loopback/repository'; +import {db} from './datasources/db.datasource'; +import {TodoRepository} from './repositories'; + +export class TodoApplication extends RepositoryMixin(RestApplication) { + constructor(options?: ApplicationConfig) { + super(options); + this.setupControllers(); + this.setupRepositories(); + } + + setupControllers() { + this.controller(TodoController); + this.controller(PingController); + } + + setupRepositories() { + // This will allow you to test your application without needing to + // use the "real" datasource! + const datasource = + this.options && this.options.datasource + ? new DataSourceConstructor(this.options.datasource) + : db; + this.bind('datasource').to(datasource); + this.repository(TodoRepository); + } +} +``` + +### Try it out +Now that your app is ready to go, try it out with your favourite REST client! +Start the app (`npm start`) and then make some REST requests: +- `POST /todo` with a body of `{ "title": "get the milk" }` +- `GET /todo/1` and see if you get your Todo object back. +- `PATCH /todo/1` with a body of `{ "desc": "need milk for cereal" }` + +### Stuck? +Check out our [Gitter channel](https://gitter.im/strongloop/loopback) and ask +for help with this tutorial! + +### Bugs/Feedback +Open an issue in this repository **OR** on [loopback-next](https://github.com/strongloop/loopback-next) and we'll take a look!