@loopback/example-lb3-application 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.prettierignore +2 -0
  2. package/.prettierrc +7 -0
  3. package/.vscode/settings.json +20 -0
  4. package/.vscode/tasks.json +29 -0
  5. package/CHANGELOG.md +626 -0
  6. package/LICENSE +25 -0
  7. package/README.md +560 -0
  8. package/dist/__tests__/acceptance/home-page.acceptance.d.ts +1 -0
  9. package/dist/__tests__/acceptance/home-page.acceptance.js +38 -0
  10. package/dist/__tests__/acceptance/home-page.acceptance.js.map +1 -0
  11. package/dist/__tests__/acceptance/lb3app.acceptance.d.ts +1 -0
  12. package/dist/__tests__/acceptance/lb3app.acceptance.js +200 -0
  13. package/dist/__tests__/acceptance/lb3app.acceptance.js.map +1 -0
  14. package/dist/__tests__/acceptance/test-helper.d.ts +11 -0
  15. package/dist/__tests__/acceptance/test-helper.js +33 -0
  16. package/dist/__tests__/acceptance/test-helper.js.map +1 -0
  17. package/dist/application.d.ts +187 -0
  18. package/dist/application.js +41 -0
  19. package/dist/application.js.map +1 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +38 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/migrate.d.ts +1 -0
  24. package/dist/migrate.js +25 -0
  25. package/dist/migrate.js.map +1 -0
  26. package/dist/openapi-spec.d.ts +1 -0
  27. package/dist/openapi-spec.js +28 -0
  28. package/dist/openapi-spec.js.map +1 -0
  29. package/dist/sequence.d.ts +3 -0
  30. package/dist/sequence.js +12 -0
  31. package/dist/sequence.js.map +1 -0
  32. package/dist/server.d.ts +15 -0
  33. package/dist/server.js +58 -0
  34. package/dist/server.js.map +1 -0
  35. package/lb3app/common/models/coffee-shop.js +44 -0
  36. package/lb3app/common/models/coffee-shop.json +30 -0
  37. package/lb3app/server/boot/authentication.js +11 -0
  38. package/lb3app/server/boot/create-sample-models.js +28 -0
  39. package/lb3app/server/config.json +21 -0
  40. package/lb3app/server/datasources.json +6 -0
  41. package/lb3app/server/middleware.json +9 -0
  42. package/lb3app/server/model-config.json +39 -0
  43. package/lb3app/server/server.js +17 -0
  44. package/lb3app/test/acceptance.js +107 -0
  45. package/lb3app/test/authentication.js +135 -0
  46. package/lb3app/test/integration.js +75 -0
  47. package/package.json +78 -0
  48. package/public/index.html +74 -0
  49. package/public/lb3-index.html +5 -0
  50. package/src/__tests__/acceptance/home-page.acceptance.ts +44 -0
  51. package/src/__tests__/acceptance/lb3app.acceptance.ts +244 -0
  52. package/src/__tests__/acceptance/test-helper.ts +40 -0
  53. package/src/application.ts +44 -0
  54. package/src/index.ts +35 -0
  55. package/src/migrate.ts +25 -0
  56. package/src/openapi-spec.ts +28 -0
  57. package/src/sequence.ts +8 -0
  58. package/src/server.ts +71 -0
  59. package/tsconfig.json +36 -0
package/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) IBM Corp. 2019.
2
+ Node module: @loopback/example-lb3-application
3
+ This project is licensed under the MIT License, full text below.
4
+
5
+ --------
6
+
7
+ MIT license
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in
17
+ all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,560 @@
1
+ # @loopback/example-lb3-application
2
+
3
+ This example demonstrates how to mount your existing LoopBack 3 (LB3)
4
+ application on a new LoopBack 4 (LB4) project and how to move the middleware
5
+ from the LB3 application to a common location so that both the LB3 and LB4
6
+ applications can use them.
7
+
8
+ ## Mounting LB3 app on LB4 app
9
+
10
+ 1. Create a new LoopBack 4 project using `lb4 app`.
11
+
12
+ ```
13
+ $ lb4 app
14
+ ```
15
+
16
+ Fill out the prompts as they fit your project and leave all features enabled.
17
+
18
+ 2. Create a new directory `lb3app` from the root of your LoopBack 4 application
19
+ and copy your existing LoopBack 3 application there. You should end up with
20
+ the following directory layout:
21
+
22
+ ```
23
+ lb3app/
24
+ # LoopBack 3 application in JavaScript
25
+ common/
26
+ models/
27
+ # LB3 model files
28
+ server/
29
+ boot/
30
+ # LB3 boot scripts
31
+ public/
32
+ # front-end assets (LB4 way)
33
+ src/
34
+ # LoopBack 4 application in TypeScript
35
+ ```
36
+
37
+ 3. Move LB3 dependencies to the main package.json file and remove
38
+ `lb3app/package.json`, `lb3app/node_modules/`, and`lb3app/package-lock.json`,
39
+ if it exists. Typically you will need to add the following entries, plus any
40
+ connectors or components you are using in your LB3 application.
41
+
42
+ ```json
43
+ {
44
+ "compression": "^1.7.4",
45
+ "cors": "^2.8.5",
46
+ "helmet": "^3.16.0",
47
+ "loopback": "^3.25.1",
48
+ "loopback-boot": "^3.3.0"
49
+ }
50
+ ```
51
+
52
+ Note: make sure to use `loopback-boot@3.2.1` or higher.
53
+
54
+ Run `npm install` from the root of your LB4 project to install the LB3
55
+ dependencies.
56
+
57
+ 4. Disable error handling in your LB3 app, leave it for the new LB4 app.
58
+
59
+ - Remove `lb3app/server/middleware.development.json`
60
+ - Edit `lb3app/server/middleware.json` and remove the following two entries:
61
+ - `final` >> `loopback#urlNotFound`
62
+ - `final:after` >> `strong-error-handler`
63
+ - Remove `strong-error-handler` from `package.json` dependencies.
64
+ - In `lb3app/server/config.json`, if `"handleErrors": false` is in
65
+ `remoting`, move it to `rest`.
66
+
67
+ 5. Move your front-end files from `lb3app/client` to `public/` directory and
68
+ disable static assets in your LB3 app by removing the following entry in
69
+ `lb3app/server/middleware.json`:
70
+
71
+ - `files` >> `loopback#static`
72
+
73
+ Also remove `lb3app/server/boot/root.js`, since the main page will be served
74
+ by the LoopBack 4 project.
75
+
76
+ 6. Remove `lb3app/server/component-config.json` to disable LoopBack 3's
77
+ explorer. The LoopBack 4 explorer will be used instead.
78
+
79
+ 7. Install and configure `@loopback/booter-lb3app` to boot and mount the LB3
80
+ application:
81
+
82
+ 1. `npm install --save @loopback/booter-lb3app`
83
+
84
+ 2. Import the component at the top of your `src/application.ts` file.
85
+
86
+ ```ts
87
+ import {Lb3AppBooterComponent} from '@loopback/booter-lb3app';
88
+ ```
89
+
90
+ 3. Register the component in Application's constructor:
91
+
92
+ ```ts
93
+ this.component(Lb3AppBooterComponent);
94
+ ```
95
+
96
+ Start the new LB4 application
97
+
98
+ ```sh
99
+ $ npm start
100
+ Server is running at http://127.0.0.1:3000
101
+ ```
102
+
103
+ Open the URL printed to console to view your front-end served from `public`
104
+ directory or go to `http://127.0.0.1:3000/explorer` to explore the REST API.
105
+
106
+ The LB3 application is now successfully mounted on a LB4 application, but we can
107
+ further optimize the setup so that only the bare neccessary artifacts from the
108
+ LoopBack 3 application remain. This includes moving almost all of the middleware
109
+ to a common location so that they are shared by both the LoopBack 3 and the
110
+ LoopBack 4 apps.
111
+
112
+ ## Migrating Express middleware from LB3 app
113
+
114
+ 1. Update config.json
115
+
116
+ First off, edit the LB3 app's `config.json` file.
117
+
118
+ Remove these properties, as they are not required anymore:
119
+
120
+ ```json
121
+ "host": "0.0.0.0",
122
+ "port": 3000,
123
+ ```
124
+
125
+ Change `restApiRoot` to `/` or any path where you would like the LB3 app to
126
+ be mounted; that's relative to the LB4 app's REST API path, which defaults to
127
+ `/api`.
128
+
129
+ And then add `"handleUnknownPaths": false` to the `rest` property, this will
130
+ prevent the LB3 REST api from sending a 404 response for requests it cannot
131
+ handle.
132
+
133
+ The `config.json` file should now look like this:
134
+
135
+ ```json
136
+ {
137
+ "restApiRoot": "/",
138
+ "remoting": {
139
+ "context": false,
140
+ "rest": {
141
+ "handleErrors": false,
142
+ "handleUnknownPaths": false,
143
+ "normalizeHttpPath": false,
144
+ "xml": false
145
+ },
146
+ "json": {
147
+ "strict": false,
148
+ "limit": "100kb"
149
+ },
150
+ "urlencoded": {
151
+ "extended": true,
152
+ "limit": "100kb"
153
+ },
154
+ "cors": false
155
+ }
156
+ }
157
+ ```
158
+
159
+ 2. Configure the base Express app
160
+
161
+ We will be using a base Express app (`src/server.ts`) for mounting the LB4
162
+ app as described in
163
+ "[Creating an Express Application with LoopBack REST API](https://loopback.io/doc/en/lb4/express-with-lb4-rest-tutorial.html)"
164
+ guide.
165
+
166
+ Migrate the LB3 app's middleware from its `middleware.json` file to this
167
+ Express app, except the one from the `routes` phase (there is a
168
+ [pending task](https://github.com/loopbackio/loopback-next/issues/4181) to
169
+ complete the support for this middleware).
170
+
171
+ Each root property in the `middleware.json` object represents a middleware
172
+ phase, extract the relevant middleware and load them in the Express app in
173
+ order.
174
+
175
+ An entry like `"compression": {}` translates to `compression()`, and
176
+ `loopback#favicon` translates to `loopback.favicon()` in TypeScript. For more
177
+ details about `middleware.json`, refer to
178
+ [its documentation](https://loopback.io/doc/en/lb3/middleware.json.html).
179
+
180
+ The `middleware.json` file should look like this now:
181
+
182
+ ```js
183
+ {
184
+ "routes": {
185
+ "loopback#rest": {
186
+ "paths": [
187
+ "${restApiRoot}"
188
+ ]
189
+ }
190
+ }
191
+ }
192
+ ```
193
+
194
+ The middleware mounted in the Express app will be shared by both LB3 and LB4
195
+ apps.
196
+
197
+ Move any static files from the LB3 app to the `public` directory of the
198
+ Express app. Move any non-REST routes defined anywhere in the LB3 app to the
199
+ Express app.
200
+
201
+ This is what the `src/server.ts` file will look like:
202
+
203
+ ```ts
204
+ import {ApplicationConfig} from '@loopback/core';
205
+ import {once} from 'events';
206
+ import express, {Request, Response} from 'express';
207
+ import * as http from 'http';
208
+ import {AddressInfo} from 'net';
209
+ import * as path from 'path';
210
+ // Replace CoffeeShopApplication with the name of your application
211
+ import {CoffeeShopApplication} from './application';
212
+
213
+ const loopback = require('loopback');
214
+ const compression = require('compression');
215
+ const cors = require('cors');
216
+ const helmet = require('helmet');
217
+
218
+ export class ExpressServer {
219
+ private app: express.Application;
220
+ public readonly lbApp: CoffeeShopApplication;
221
+ public server?: http.Server;
222
+ public url: String;
223
+
224
+ constructor(options: ApplicationConfig = {}) {
225
+ this.app = express();
226
+ this.lbApp = new CoffeeShopApplication(options);
227
+
228
+ // Middleware migrated from LoopBack 3
229
+ this.app.use(loopback.favicon());
230
+ this.app.use(compression());
231
+ this.app.use(cors());
232
+ this.app.use(helmet());
233
+
234
+ // Mount the LB4 REST API
235
+ this.app.use('/api', this.lbApp.requestHandler);
236
+
237
+ // Custom Express routes
238
+ this.app.get('/ping', function (_req: Request, res: Response) {
239
+ res.send('pong');
240
+ });
241
+
242
+ // Serve static files in the public folder
243
+ this.app.use(express.static(path.join(__dirname, '../public')));
244
+ }
245
+
246
+ public async boot() {
247
+ await this.lbApp.boot();
248
+ }
249
+
250
+ public async start() {
251
+ await this.lbApp.start();
252
+ const port = this.lbApp.restServer.config.port || 3000;
253
+ const host = this.lbApp.restServer.config.host || '127.0.0.1';
254
+ this.server = this.app.listen(port, host);
255
+ await once(this.server, 'listening');
256
+ const add = <AddressInfo>this.server.address();
257
+ this.url = `http://${add.address}:${add.port}`;
258
+ }
259
+
260
+ public async stop() {
261
+ if (!this.server) return;
262
+ await this.lbApp.stop();
263
+ this.server.close();
264
+ await once(this.server, 'close');
265
+ this.server = undefined;
266
+ }
267
+ }
268
+ ```
269
+
270
+ 3. Update `src/index.ts`
271
+
272
+ The Express app will replace the `CoffeeShopApplication` as the entry point
273
+ for the program, modify the `src/index.ts` file accordingly.
274
+
275
+ ```ts
276
+ import {ApplicationConfig} from '@loopback/core';
277
+ import {ExpressServer} from './server';
278
+
279
+ export {ApplicationConfig, ExpressServer};
280
+
281
+ export async function main(options: ApplicationConfig = {}) {
282
+ const server = new ExpressServer(options);
283
+ await server.boot();
284
+ await server.start();
285
+ console.log(`Server is running at ${server.url}`);
286
+ }
287
+ ```
288
+
289
+ 4. Next, modify the application config in `src/index.ts` file to prevent the LB4
290
+ app from listening, by adding `listenOnStart: false` in `config.rest` object.
291
+ The `config` object should now look like this:
292
+
293
+ ```ts
294
+ const config = {
295
+ rest: {
296
+ port: +(process.env.PORT ?? 3000),
297
+ host: process.env.HOST ?? 'localhost',
298
+ openApiSpec: {
299
+ // useful when used with OpenAPI-to-GraphQL to locate your application
300
+ setServersFromRequest: true,
301
+ },
302
+ listenOnStart: false,
303
+ },
304
+ };
305
+ ```
306
+
307
+ Then, in the `bootOptions` of the `CoffeeShopApplication` class, add the
308
+ `lb3app` to configure the path of the LB3 APIs.
309
+
310
+ ```js
311
+ lb3app: {
312
+ mode: 'fullApp';
313
+ }
314
+ ```
315
+
316
+ `this.bootOptions` should now look like this:
317
+
318
+ ```ts
319
+ this.bootOptions = {
320
+ controllers: {
321
+ // Customize ControllerBooter Conventions here
322
+ dirs: ['controllers'],
323
+ extensions: ['.controller.js'],
324
+ nested: true,
325
+ },
326
+ lb3app: {
327
+ mode: 'fullApp',
328
+ },
329
+ };
330
+ ```
331
+
332
+ Start the app:
333
+
334
+ ```sh
335
+ $ npm start
336
+ ```
337
+
338
+ Load [http://localhost:3000/](http://localhost:3000/) on your browser. This will
339
+ load the Express app, with mounted LB3 and LB4 applications.
340
+
341
+ ## Running LB3 tests from LB4
342
+
343
+ You can run tests in an LoopBack 3 application from the LoopBack 4 application
344
+ it mounted on with command `npm test`.
345
+
346
+ We want the LoopBack 3 tests to use the LoopBack 4 server rather than the
347
+ LoopBack 3 application. The following guide shows how to run
348
+
349
+ - acceptance-level tests making HTTP calls to invoke application logic. e.g.
350
+ `POST /users/login`
351
+ - integration-level tests that are using JS API to call application logic. e.g.
352
+ `MyModel.create()`
353
+
354
+ ### Adding LB3 Test Path in Command
355
+
356
+ In order to run LoopBack 3's tests from their current folder, add LB3 tests'
357
+ path to `test` entry in package.json:
358
+
359
+ - `"test": "lb-mocha \"dist/**tests**/*_/_.js\" \"lb3app/test/*.js\""`
360
+
361
+ In this case, the test folder is
362
+ [`/lb3app/test`](https://github.com/loopbackio/loopback-next/tree/spike/lb3test/examples/lb3-application/lb3app/test)
363
+ from the root of the LoopBack 4 project.
364
+
365
+ This will run LoopBack 4 tests first then LoopBack 3 tests.
366
+
367
+ _To emphasize the setup steps and separate them from the test case details, all
368
+ the comprehensive test code are extracted into function `runTests`._
369
+
370
+ ### Running Acceptance Tests
371
+
372
+ First, move any LoopBack 3 test dependencies to `package.json`'s devDependencies
373
+ and run:
374
+
375
+ ```sh
376
+ npm install
377
+ ```
378
+
379
+ In your test file:
380
+
381
+ 1. When launch the Express server
382
+
383
+ - 1.1 Update to use the Express server when doing requests:
384
+
385
+ ```ts
386
+ // can use lb4's testlab's supertest as the dependency is already installed
387
+ const {supertest} = require('@loopback/testlab');
388
+ const assert = require('assert');
389
+ const should = require('should');
390
+ const {ExpressServer} = require('../../dist/server');
391
+
392
+ let app;
393
+
394
+ function jsonForExpressApp(verb, url) {
395
+ // use the express server, it mounts LoopBack 3 apis to
396
+ // base path '/api'
397
+ return supertest(app.server)
398
+ [verb]('/api' + url)
399
+ .set('Content-Type', 'application/json')
400
+ .set('Accept', 'application/json')
401
+ .expect('Content-Type', /json/);
402
+ }
403
+ ```
404
+
405
+ - 1.2 Boot and start the Express app in your before hook, and stop the app in
406
+ the after hook:
407
+
408
+ ```ts
409
+ describe('LoopBack 3 style tests - Launch Express server', function () {
410
+ before(async function () {
411
+ app = new ExpressServer();
412
+ await app.boot();
413
+ await app.start();
414
+ });
415
+
416
+ after(async () => {
417
+ await app.stop();
418
+ });
419
+
420
+ // your tests here
421
+ runTests();
422
+ });
423
+ ```
424
+
425
+ 2. When launch the LoopBack 4 application
426
+
427
+ - 2.1 Update to use the LoopBack 4 server when doing requests:
428
+
429
+ ```ts
430
+ // can use lb4's testlab's supertest as the dependency is already installed
431
+ const {supertest} = require('@loopback/testlab');
432
+ const assert = require('assert');
433
+ const should = require('should');
434
+ const {CoffeeShopApplication} = require('../../dist/application');
435
+
436
+ let app;
437
+
438
+ function jsonForLB4(verb, url) {
439
+ // use the lb4 app's rest server
440
+ return supertest(app.restServer.url)
441
+ [verb](url)
442
+ .set('Content-Type', 'application/json')
443
+ .set('Accept', 'application/json')
444
+ .expect('Content-Type', /json/);
445
+ }
446
+ ```
447
+
448
+ - 2.2 Boot and start the LoopBack 4 app in your before hook, and stop the app in
449
+ the after hook:
450
+
451
+ ```ts
452
+ describe('LoopBack 3 style tests - launch LB4 app', function () {
453
+ before(async function () {
454
+ app = new CoffeeShopApplication();
455
+ await app.boot();
456
+ await app.start();
457
+ });
458
+
459
+ after(async () => {
460
+ await app.stop();
461
+ });
462
+
463
+ // your tests here
464
+ runTests();
465
+ });
466
+ ```
467
+
468
+ Example of this use can be seen in
469
+ [`test/acceptance.js`](https://github.com/loopbackio/loopback-next/tree/master/examples/lb3-application/lb3app/test/acceptance.js)
470
+ which has the same tests as
471
+ [`src/__tests__/acceptance/lb3app.acceptance.ts`](https://github.com/loopbackio/loopback-next/blob/spike/lb3test/examples/lb3-application/src/__tests__/acceptance/lb3app.acceptance.ts),
472
+ but in LB3 style. And
473
+ [`test/authentication.js`](https://github.com/loopbackio/loopback-next/tree/master/examples/lb3-application/lb3app/test/authentication.js)
474
+
475
+ Now when you run `npm test` your LoopBack 3 tests should be run along with any
476
+ LoopBack 4 tests you have.
477
+
478
+ Optional: Another option is to migrate your tests to use LoopBack 4 style of
479
+ testing, similar to `src/__tests__/acceptance/lb3app.acceptance.ts`.
480
+ Documentation for LoopBack testing can be found in
481
+ https://loopback.io/doc/en/lb4/Testing-your-application.html.
482
+
483
+ ## Running Integration Tests
484
+
485
+ For the integration tests, LoopBack 3 models were bound to the LoopBack 4
486
+ application in order to allow JavaScript API to call application logic such as
487
+ `Model.create()`. This can be seen in
488
+ [`packages/booter-lb3app/src/lb3app.booter.ts`](https://github.com/loopbackio/loopback-next/blob/spike/lb3test/packages/booter-lb3app/src/lb3app.booter.ts#L76-L85).
489
+
490
+ In order to retrieve the model from the application's context, `get()` can be
491
+ used as follows:
492
+
493
+ ```ts
494
+ describe('LoopBack 3 style integration tests', function () {
495
+ let app;
496
+ let CoffeeShop;
497
+
498
+ before(async function () {
499
+ // If launch the LoopBack 4 application
500
+ // app = new CoffeeShopApplication();
501
+ app = new ExpressServer();
502
+ await app.boot();
503
+ await app.start();
504
+ });
505
+
506
+ before(() => {
507
+ // follow the syntax: lb3-models.{ModelName}
508
+ // If launch the LoopBack 4 application
509
+ // CoffeeShop = await app.get('lb3-models.CoffeeShop');
510
+ CoffeeShop = await app.lbApp.get('lb3-models.CoffeeShop');
511
+ });
512
+
513
+ after(async () => {
514
+ await app.stop();
515
+ });
516
+
517
+ // your tests here
518
+ runTests();
519
+ });
520
+ ```
521
+
522
+ The syntax for LB3 model's binding key is `lb3-models.{model name}`.
523
+
524
+ Additionally, LB3 datasources are also bound to the LB4 application's context
525
+ and can be retrieved with a key in the syntax `lb3-datasources.{ds name}`.
526
+
527
+ Example integration tests can be found in
528
+ [`examples/lb3-application/lb3app/test/integration.js`](https://github.com/loopbackio/loopback-next/tree/master/examples/lb3-application/lb3app/test/integration.js).
529
+
530
+ Example authentication tests can be found in
531
+ [`examples/lb3-application/lb3app/test/authentication.js`](https://github.com/loopbackio/loopback-next/tree/master/examples/lb3-application/lb3app/test/authentication.js).
532
+
533
+ ## Need help?
534
+
535
+ Check out our
536
+ [Slack](https://join.slack.com/t/loopbackio/shared_invite/zt-8lbow73r-SKAKz61Vdao~_rGf91pcsw)
537
+ and ask for help with this tutorial.
538
+
539
+ ## Bugs/Feedback
540
+
541
+ Open an issue in [loopback-next](https://github.com/loopbackio/loopback-next)
542
+ and we'll take a look.
543
+
544
+ ## Contributions
545
+
546
+ - [Guidelines](https://github.com/loopbackio/loopback-next/blob/master/docs/CONTRIBUTING.md)
547
+ - [Join the team](https://github.com/loopbackio/loopback-next/issues/110)
548
+
549
+ ## Tests
550
+
551
+ Run `npm test` from the root folder.
552
+
553
+ ## Contributors
554
+
555
+ See
556
+ [all contributors](https://github.com/loopbackio/loopback-next/graphs/contributors).
557
+
558
+ ## License
559
+
560
+ MIT