├── examples ├── lb3-express │ ├── .eslintignore │ ├── lib │ │ ├── .eslintrc │ │ ├── .yo-rc.json │ │ ├── client │ │ │ └── README.md │ │ ├── server │ │ │ ├── component-config.json │ │ │ ├── boot │ │ │ │ ├── authentication.js │ │ │ │ ├── root.js │ │ │ │ └── create-sample-models.js │ │ │ ├── middleware.development.json │ │ │ ├── datasources.json │ │ │ ├── config.json │ │ │ ├── model-config.json │ │ │ ├── server.js │ │ │ └── middleware.json │ │ ├── .gitignore │ │ └── common │ │ │ └── models │ │ │ └── coffee-shop.json │ ├── lambda-wrapper.js │ ├── .editorconfig │ ├── .eslintrc.json │ ├── package.json │ ├── README.md │ ├── serverless.yml │ └── .snyk └── lb4-express │ ├── .prettierignore │ ├── src │ ├── __tests__ │ │ ├── mocha.opts │ │ └── acceptance │ │ │ ├── ping.controller.acceptance.ts │ │ │ ├── test-helper.ts │ │ │ ├── express.acceptance.ts │ │ │ └── note.acceptance.ts │ ├── models │ │ ├── index.ts │ │ └── note.model.ts │ ├── controllers │ │ ├── index.ts │ │ ├── ping.controller.ts │ │ └── note.controller.ts │ ├── datasources │ │ ├── index.ts │ │ └── ds.datasource.ts │ ├── observers │ │ ├── index.ts │ │ └── hello.observer.ts │ ├── repositories │ │ ├── index.ts │ │ └── note.repository.ts │ ├── migrate.ts │ ├── index.ts │ ├── lambda-wrapper.js │ ├── sequence.ts │ ├── application.ts │ └── server.ts │ ├── .prettierrc │ ├── .eslintrc.js │ ├── data │ └── ds.json │ ├── tsconfig.json │ ├── .vscode │ ├── settings.json │ └── tasks.json │ ├── public │ ├── express.html │ ├── notes.html │ └── index.html │ ├── LICENSE │ ├── serverless.yml │ ├── package.json │ ├── README.md │ └── CHANGELOG.md ├── LICENSE ├── .gitignore └── README.md /examples/lb3-express/.eslintignore: -------------------------------------------------------------------------------- 1 | /lib/client/ 2 | -------------------------------------------------------------------------------- /examples/lb4-express/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | *.json 3 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "loopback" 3 | } -------------------------------------------------------------------------------- /examples/lb3-express/lib/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-loopback": {} 3 | } -------------------------------------------------------------------------------- /examples/lb4-express/src/__tests__/mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive 2 | --require source-map-support/register 3 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/client/README.md: -------------------------------------------------------------------------------- 1 | ## Client 2 | 3 | This is the place for your application front-end files. 4 | -------------------------------------------------------------------------------- /examples/lb4-express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "singleQuote": true, 4 | "printWidth": 80, 5 | "trailingComma": "all", 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/component-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "loopback-component-explorer": { 3 | "mountPath": "/explorer", 4 | "generateOperationScopedModels": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/boot/authentication.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function enableAuthentication(server) { 4 | // enable authentication 5 | server.enableAuth(); 6 | }; 7 | -------------------------------------------------------------------------------- /examples/lb3-express/lambda-wrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const serverless = require ('serverless-http'); 4 | 5 | const app = require('./lib/server/server'); 6 | 7 | exports.handler = serverless(app); 8 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/middleware.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "final:after": { 3 | "strong-error-handler": { 4 | "params": { 5 | "debug": true, 6 | "log": true 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.dat 3 | *.iml 4 | *.log 5 | *.out 6 | *.pid 7 | *.seed 8 | *.sublime-* 9 | *.swo 10 | *.swp 11 | *.tgz 12 | *.xml 13 | .DS_Store 14 | .idea 15 | .project 16 | .strong-pm 17 | coverage 18 | node_modules 19 | npm-debug.log 20 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/boot/root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(server) { 4 | // Install a `/` route that returns server status 5 | var router = server.loopback.Router(); 6 | router.get('/', server.loopback.status()); 7 | server.use(router); 8 | }; 9 | -------------------------------------------------------------------------------- /examples/lb4-express/src/models/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | export * from './note.model'; 7 | -------------------------------------------------------------------------------- /examples/lb4-express/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | export * from './note.controller'; 7 | -------------------------------------------------------------------------------- /examples/lb4-express/src/datasources/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | export * from './ds.datasource'; 7 | -------------------------------------------------------------------------------- /examples/lb4-express/src/observers/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | export * from './hello.observer'; 7 | -------------------------------------------------------------------------------- /examples/lb4-express/src/repositories/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | export * from './note.repository'; 7 | -------------------------------------------------------------------------------- /examples/lb4-express/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = { 7 | extends: ['@loopback/eslint-config'], 8 | }; 9 | -------------------------------------------------------------------------------- /examples/lb4-express/data/ds.json: -------------------------------------------------------------------------------- 1 | { 2 | "ids": { 3 | "Note": 3 4 | }, 5 | "models": { 6 | "Note": { 7 | "1": "{\"title\":\"Things I need to buy\",\"content\":\"milk, cereal, and waffles\",\"id\":1}", 8 | "2": "{\"title\":\"Great frameworks\",\"content\":\"LoopBack is a great framework\",\"id\":2}" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/lb3-express/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | -------------------------------------------------------------------------------- /examples/lb4-express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema":"http://json.schemastore.org/tsconfig", 3 | "extends":"@loopback/build/config/tsconfig.common.json", 4 | "compilerOptions": { 5 | "outDir":"dist", 6 | "rootDir":"src", 7 | "composite":false, 8 | "allowJs": true 9 | }, 10 | "include":[ 11 | "src/**/*", 12 | "src/**/*.json" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/datasources.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": { 3 | "name": "db", 4 | "connector": "memory" 5 | }, 6 | "mysql": { 7 | "name": "mysql", 8 | "connector": "mysql", 9 | "host": "${DB_HOST}", 10 | "port": "${DB_PORT}", 11 | "database": "${DB_NAME}", 12 | "connectTimeout": 600000, 13 | "user": "${DB_USERNAME}", 14 | "password": "${DB_PASSWORD}" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/common/models/coffee-shop.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CoffeeShop", 3 | "base": "PersistedModel", 4 | "idInjection": true, 5 | "forceId": false, 6 | "options": { 7 | "validateUpsert": true 8 | }, 9 | "properties": { 10 | "name": { 11 | "type": "string", 12 | "required": true 13 | }, 14 | "city": { 15 | "type": "string" 16 | } 17 | }, 18 | "validations": [], 19 | "relations": {}, 20 | "acls": [], 21 | "methods": {} 22 | } 23 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "restApiRoot": "/api", 3 | "host": "0.0.0.0", 4 | "port": 3000, 5 | "remoting": { 6 | "context": false, 7 | "rest": { 8 | "handleErrors": false, 9 | "normalizeHttpPath": false, 10 | "xml": false 11 | }, 12 | "json": { 13 | "strict": false, 14 | "limit": "100kb" 15 | }, 16 | "urlencoded": { 17 | "extended": true, 18 | "limit": "100kb" 19 | }, 20 | "cors": false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/lb4-express/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [80], 3 | "editor.tabCompletion": "on", 4 | "editor.tabSize": 2, 5 | "editor.trimAutoWhitespace": true, 6 | "editor.formatOnSave": true, 7 | 8 | "files.exclude": { 9 | "**/.DS_Store": true, 10 | "**/.git": true, 11 | "**/.hg": true, 12 | "**/.svn": true, 13 | "**/CVS": true, 14 | "dist": true, 15 | }, 16 | "files.insertFinalNewline": true, 17 | "files.trimTrailingWhitespace": true, 18 | 19 | "typescript.tsdk": "./node_modules/typescript/lib" 20 | } 21 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/boot/create-sample-models.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.dataSources.mysql.automigrate('CoffeeShop', function(err) { 5 | if (err) throw err; 6 | 7 | app.models.CoffeeShop.create([{ 8 | name: 'Bel Cafe', 9 | city: 'Vancouver', 10 | }, { 11 | name: 'Three Bees Coffee House', 12 | city: 'San Mateo', 13 | }, { 14 | name: 'Caffe Artigiano', 15 | city: 'Vancouver', 16 | }], function(err, coffeeShops) { 17 | if (err) throw err; 18 | 19 | console.log('Models created: \n', coffeeShops); 20 | }); 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /examples/lb3-express/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 2017 4 | }, 5 | "env": { 6 | "node": true, 7 | "es6": true 8 | }, 9 | "extends": "eslint:recommended", 10 | "rules": { 11 | "indent": [ 12 | "error", 13 | 4 14 | ], 15 | "linebreak-style": [ 16 | "error", 17 | "unix" 18 | ], 19 | "quotes": [ 20 | "error", 21 | "single" 22 | ], 23 | "camelcase": [ 24 | "error" 25 | ], 26 | "semi": [ 27 | "warn", 28 | "always" 29 | ], 30 | "prefer-const": ["error", { 31 | "destructuring": "any", 32 | "ignoreReadBeforeAssign": false 33 | }], 34 | "strict": [ 35 | "error", 36 | "global" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/lb4-express/public/express.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LoopBack 4 REST API on Express 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |

Express

17 |

This is the main Express page. 18 | Click here for the LoopBack main page and 19 | here to say hi.

20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/lb4-express/src/repositories/note.repository.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {inject} from '@loopback/core'; 7 | import {DefaultCrudRepository} from '@loopback/repository'; 8 | import {DsDataSource} from '../datasources'; 9 | import {Note, NoteRelations} from '../models'; 10 | 11 | export class NoteRepository extends DefaultCrudRepository< 12 | Note, 13 | typeof Note.prototype.id, 14 | NoteRelations 15 | > { 16 | constructor(@inject('datasources.ds') dataSource: DsDataSource) { 17 | super(Note, dataSource); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/lb4-express/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Watch and Compile Project", 8 | "type": "shell", 9 | "command": "npm", 10 | "args": ["--silent", "run", "build:watch"], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | }, 15 | "problemMatcher": "$tsc-watch" 16 | }, 17 | { 18 | "label": "Build, Test and Lint", 19 | "type": "shell", 20 | "command": "npm", 21 | "args": ["--silent", "run", "test:dev"], 22 | "group": { 23 | "kind": "test", 24 | "isDefault": true 25 | }, 26 | "problemMatcher": ["$tsc", "$eslint-stylish", "$eslint-compact"] 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /examples/lb4-express/src/models/note.model.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {Entity, model, property} from '@loopback/repository'; 7 | 8 | @model() 9 | export class Note extends Entity { 10 | @property({ 11 | type: 'number', 12 | id: true, 13 | }) 14 | id?: number; 15 | 16 | @property({ 17 | type: 'string', 18 | required: true, 19 | }) 20 | title: string; 21 | 22 | @property({ 23 | type: 'string', 24 | }) 25 | content?: string; 26 | 27 | constructor(data?: Partial) { 28 | super(data); 29 | } 30 | } 31 | 32 | export interface NoteRelations { 33 | // describe navigational properties here 34 | } 35 | 36 | export type NoteWithRelations = Note & NoteRelations; 37 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/model-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "sources": [ 4 | "loopback/common/models", 5 | "loopback/server/models", 6 | "../common/models", 7 | "./models" 8 | ], 9 | "mixins": [ 10 | "loopback/common/mixins", 11 | "loopback/server/mixins", 12 | "../common/mixins", 13 | "./mixins" 14 | ] 15 | }, 16 | "User": { 17 | "dataSource": "db" 18 | }, 19 | "AccessToken": { 20 | "dataSource": "db", 21 | "public": false 22 | }, 23 | "ACL": { 24 | "dataSource": "db", 25 | "public": false 26 | }, 27 | "RoleMapping": { 28 | "dataSource": "db", 29 | "public": false, 30 | "options": { 31 | "strictObjectIDCoercion": true 32 | } 33 | }, 34 | "Role": { 35 | "dataSource": "db", 36 | "public": false 37 | }, 38 | "CoffeeShop": { 39 | "dataSource": "mysql", 40 | "public": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/lb4-express/src/datasources/ds.datasource.ts: -------------------------------------------------------------------------------- 1 | import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core'; 2 | import {juggler} from '@loopback/repository'; 3 | 4 | const config = { 5 | name: 'ds', 6 | connector: 'memory', 7 | }; 8 | 9 | // Observe application's life cycle to disconnect the datasource when 10 | // application is stopped. This allows the application to be shut down 11 | // gracefully. The `stop()` method is inherited from `juggler.DataSource`. 12 | // Learn more at https://loopback.io/doc/en/lb4/Life-cycle.html 13 | @lifeCycleObserver('datasource') 14 | export class DsDataSource extends juggler.DataSource 15 | implements LifeCycleObserver { 16 | static dataSourceName = 'ds'; 17 | static readonly defaultConfig = config; 18 | 19 | constructor( 20 | @inject('datasources.config.ds', {optional: true}) 21 | dsConfig: object = config, 22 | ) { 23 | super(dsConfig); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/lb4-express/src/migrate.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {ExpressServer} from './server'; 7 | 8 | export async function migrate(args: string[]) { 9 | const existingSchema = args.includes('--rebuild') ? 'drop' : 'alter'; 10 | console.log('Migrating schemas (%s existing schema)', existingSchema); 11 | 12 | const server = new ExpressServer(); 13 | await server.boot(); 14 | await server.lbApp.migrateSchema({existingSchema}); 15 | 16 | // Connectors usually keep a pool of opened connections, 17 | // this keeps the process running even after all work is done. 18 | // We need to exit explicitly. 19 | process.exit(0); 20 | } 21 | 22 | migrate(process.argv).catch(err => { 23 | console.error('Cannot migrate database schema', err); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var loopback = require('loopback'); 4 | var boot = require('loopback-boot'); 5 | 6 | var app = module.exports = loopback(); 7 | 8 | app.start = function() { 9 | // start the web server 10 | return app.listen(function() { 11 | app.emit('started'); 12 | var baseUrl = app.get('url').replace(/\/$/, ''); 13 | console.log('Web server listening at: %s', baseUrl); 14 | if (app.get('loopback-component-explorer')) { 15 | var explorerPath = app.get('loopback-component-explorer').mountPath; 16 | console.log('Browse your REST API at %s%s', baseUrl, explorerPath); 17 | } 18 | }); 19 | }; 20 | 21 | // Bootstrap the application, configure models, datasources and middleware. 22 | // Sub-apps like REST API are mounted via boot scripts. 23 | boot(app, __dirname, function(err) { 24 | if (err) throw err; 25 | 26 | // start the server if `$ node server.js` 27 | if (require.main === module) 28 | app.start(); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/lb4-express/src/__tests__/acceptance/ping.controller.acceptance.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {Client, expect} from '@loopback/testlab'; 7 | import {setupExpressApplication} from './test-helper'; 8 | import {ExpressServer} from '../../server'; 9 | 10 | describe('PingController', () => { 11 | let server: ExpressServer; 12 | let client: Client; 13 | 14 | before('setupApplication', async () => { 15 | ({server, client} = await setupExpressApplication()); 16 | }); 17 | 18 | after('closes application', async () => { 19 | await server.stop(); 20 | }); 21 | 22 | it('invokes GET /ping', async () => { 23 | const res = await client.get('/api/ping?msg=world').expect(200); 24 | expect(res.body).to.containEql({greeting: 'Hello from LoopBack'}); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/lb3-express/lib/server/middleware.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial:before": { 3 | "loopback#favicon": {} 4 | }, 5 | "initial": { 6 | "compression": {}, 7 | "cors": { 8 | "params": { 9 | "origin": true, 10 | "credentials": true, 11 | "maxAge": 86400 12 | } 13 | }, 14 | "helmet#xssFilter": {}, 15 | "helmet#frameguard": { 16 | "params": { "action": "deny" } 17 | }, 18 | "helmet#hsts": { 19 | "params": { 20 | "maxAge": 0, 21 | "includeSubdomains": true 22 | } 23 | }, 24 | "helmet#hidePoweredBy": {}, 25 | "helmet#ieNoOpen": {}, 26 | "helmet#noSniff": {}, 27 | "helmet#noCache": { 28 | "enabled": false 29 | } 30 | }, 31 | "session": {}, 32 | "auth": {}, 33 | "parse": {}, 34 | "routes": { 35 | "loopback#rest": { 36 | "paths": [ 37 | "${restApiRoot}" 38 | ] 39 | } 40 | }, 41 | "files": {}, 42 | "final": { 43 | "loopback#urlNotFound": {} 44 | }, 45 | "final:after": { 46 | "strong-error-handler": {} 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 BotBits (sm) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/lb4-express/src/observers/hello.observer.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import { 7 | lifeCycleObserver, // The decorator 8 | LifeCycleObserver, 9 | } from '@loopback/core'; 10 | 11 | /** 12 | * This class will be bound to the application as a `LifeCycleObserver` during 13 | * `boot` 14 | */ 15 | @lifeCycleObserver() 16 | export class HelloObserver implements LifeCycleObserver { 17 | events: string[] = []; 18 | /* 19 | constructor( 20 | @inject(CoreBindings.APPLICATION_INSTANCE) private app: Application, 21 | ) {} 22 | */ 23 | 24 | /** 25 | * This method will be invoked when the application starts 26 | */ 27 | async start(): Promise { 28 | this.events.push(`${new Date()}: hello-start`); 29 | } 30 | 31 | /** 32 | * This method will be invoked when the application stops 33 | */ 34 | async stop(): Promise { 35 | this.events.push(`${new Date()}: hello-stop`); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/lb4-express/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {ApplicationConfig, ExpressServer} from './server'; 7 | 8 | export * from './server'; 9 | 10 | export async function main(options: ApplicationConfig = {}) { 11 | const server = new ExpressServer(options); 12 | await server.boot(); 13 | await server.start(); 14 | console.log('Server is running at http://127.0.0.1:3000'); 15 | } 16 | 17 | if (require.main === module) { 18 | // Run the application 19 | const config = { 20 | rest: { 21 | port: +(process.env.PORT ?? 3000), 22 | host: process.env.HOST ?? 'localhost', 23 | openApiSpec: { 24 | // useful when used with OpenAPI-to-GraphQL to locate your application 25 | setServersFromRequest: true, 26 | }, 27 | // Use the LB4 application as a route. It should not be listening. 28 | listenOnStart: false, 29 | }, 30 | }; 31 | main(config).catch(err => { 32 | console.error('Cannot start the application.', err); 33 | process.exit(1); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /examples/lb4-express/src/lambda-wrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {ExpressServer} = require('./server'); 4 | const serverless = require('serverless-http'); 5 | 6 | let app; 7 | 8 | async function main(options) { 9 | const server = new ExpressServer(options); 10 | app = serverless(server.app); 11 | await server.boot(); 12 | await server.start(); 13 | console.log('Server is running at http://127.0.0.1:3000'); 14 | } 15 | 16 | exports.handler = async function handler(request, ...context) { 17 | if (app === undefined) { 18 | // Run the application 19 | const config = { 20 | rest: { 21 | port: +(process.env.PORT ? process.env.PORT : 3000), 22 | host: process.env.HOST ? process.env.HOST : 'localhost', 23 | openApiSpec: { 24 | // useful when used with OpenAPI-to-GraphQL to locate your application 25 | setServersFromRequest: true, 26 | }, 27 | // Use the LB4 application as a route. It should not be listening. 28 | listenOnStart: false, 29 | }, 30 | }; 31 | await main(config).catch(err => { 32 | console.error('Cannot start the application.', err); 33 | process.exit(1); 34 | }); 35 | } 36 | return app(request, ...context); 37 | }; 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Loopback4 2 | dist 3 | tsconfig.tsbuildinfo 4 | 5 | # Serverless 6 | .serverless 7 | 8 | # Logs 9 | logs 10 | *.log 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # TypeScript v1 declaration files 47 | typings/ 48 | 49 | # Optional npm cache directory 50 | .npm 51 | 52 | # Optional eslint cache 53 | .eslintcache 54 | 55 | # Optional REPL history 56 | .node_repl_history 57 | 58 | # Output of 'npm pack' 59 | *.tgz 60 | 61 | # Yarn Integrity file 62 | .yarn-integrity 63 | 64 | # dotenv environment variables file 65 | .env 66 | 67 | # next.js build output 68 | .next 69 | -------------------------------------------------------------------------------- /examples/lb4-express/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) IBM Corp. 2019. 2 | Node module: @loopback/example-express-composition 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. 26 | -------------------------------------------------------------------------------- /examples/lb4-express/src/__tests__/acceptance/test-helper.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {givenHttpServerConfig, Client, supertest} from '@loopback/testlab'; 7 | import {NoteApplication} from '../../application'; 8 | import {ExpressServer} from '../../server'; 9 | import {Note} from '../../models/note.model'; 10 | 11 | export async function setupExpressApplication(): Promise { 12 | const server = new ExpressServer({rest: givenHttpServerConfig()}); 13 | await server.boot(); 14 | await server.start(); 15 | 16 | const lbApp = server.lbApp; 17 | 18 | const client = supertest(server.app); 19 | 20 | return {server, client, lbApp}; 21 | } 22 | 23 | export interface AppWithClient { 24 | server: ExpressServer; 25 | client: Client; 26 | lbApp: NoteApplication; 27 | } 28 | 29 | /** 30 | * Generate a complete Note object for use with tests. 31 | * @param A partial (or complete) Note object. 32 | */ 33 | export function givenNote(note?: Partial) { 34 | const data = Object.assign( 35 | { 36 | title: 'start essay', 37 | content: 'write thesis', 38 | }, 39 | note, 40 | ); 41 | return new Note(data); 42 | } 43 | -------------------------------------------------------------------------------- /examples/lb4-express/src/sequence.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {inject} from '@loopback/core'; 7 | import { 8 | FindRoute, 9 | InvokeMethod, 10 | ParseParams, 11 | Reject, 12 | RequestContext, 13 | RestBindings, 14 | Send, 15 | SequenceHandler, 16 | } from '@loopback/rest'; 17 | 18 | const SequenceActions = RestBindings.SequenceActions; 19 | 20 | export class MySequence implements SequenceHandler { 21 | constructor( 22 | @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute, 23 | @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams, 24 | @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod, 25 | @inject(SequenceActions.SEND) public send: Send, 26 | @inject(SequenceActions.REJECT) public reject: Reject, 27 | ) {} 28 | 29 | async handle(context: RequestContext) { 30 | try { 31 | const {request, response} = context; 32 | const route = this.findRoute(request); 33 | const args = await this.parseParams(request, route); 34 | const result = await this.invoke(route, args); 35 | this.send(response, result); 36 | } catch (err) { 37 | this.reject(context, err); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/lb4-express/public/notes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Notes 5 | 6 | 7 | 27 | 28 | 29 | 30 |

Notes

31 | 32 | 33 | 34 | 35 | 36 | 37 |
TitleContent
38 | 39 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/lb4-express/src/application.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {BootMixin} from '@loopback/boot'; 7 | import {ApplicationConfig} from '@loopback/core'; 8 | import {RepositoryMixin} from '@loopback/repository'; 9 | import {RestApplication} from '@loopback/rest'; 10 | import {RestExplorerComponent} from '@loopback/rest-explorer'; 11 | import {ServiceMixin} from '@loopback/service-proxy'; 12 | import path from 'path'; 13 | import {MySequence} from './sequence'; 14 | 15 | export {ApplicationConfig}; 16 | 17 | export class NoteApplication extends BootMixin( 18 | ServiceMixin(RepositoryMixin(RestApplication)), 19 | ) { 20 | constructor(options: ApplicationConfig = {}) { 21 | super(options); 22 | 23 | // Set up the custom sequence 24 | this.sequence(MySequence); 25 | 26 | // Set up default home page 27 | this.static('/', path.join(__dirname, '../public')); 28 | 29 | this.component(RestExplorerComponent); 30 | 31 | this.projectRoot = __dirname; 32 | // Customize @loopback/boot Booter Conventions here 33 | this.bootOptions = { 34 | controllers: { 35 | // Customize ControllerBooter Conventions here 36 | dirs: ['controllers'], 37 | extensions: ['.controller.js'], 38 | nested: true, 39 | }, 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/lb4-express/src/controllers/ping.controller.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {Request, RestBindings, get, ResponseObject} from '@loopback/rest'; 7 | import {inject} from '@loopback/core'; 8 | 9 | /** 10 | * OpenAPI response for ping() 11 | */ 12 | const PING_RESPONSE: ResponseObject = { 13 | description: 'Ping Response', 14 | content: { 15 | 'application/json': { 16 | schema: { 17 | type: 'object', 18 | title: 'PingResponse', 19 | properties: { 20 | greeting: {type: 'string'}, 21 | date: {type: 'string'}, 22 | url: {type: 'string'}, 23 | headers: { 24 | type: 'object', 25 | properties: { 26 | 'Content-Type': {type: 'string'}, 27 | }, 28 | additionalProperties: true, 29 | }, 30 | }, 31 | }, 32 | }, 33 | }, 34 | }; 35 | 36 | /** 37 | * A simple controller to bounce back http requests 38 | */ 39 | export class PingController { 40 | constructor(@inject(RestBindings.Http.REQUEST) private req: Request) {} 41 | 42 | // Map to `GET /ping` 43 | @get('/ping', { 44 | responses: { 45 | '200': PING_RESPONSE, 46 | }, 47 | }) 48 | ping(): object { 49 | // Reply with a greeting, the current time, the url, and request headers 50 | return { 51 | greeting: 'Hello from LoopBack', 52 | date: new Date(), 53 | url: this.req.url, 54 | headers: Object.assign({}, this.req.headers), 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/lb3-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-loopback", 3 | "version": "0.0.0", 4 | "description": "Sample serverless project using loopback.", 5 | "author": "BotBits (sm) (https://github.com/botbits)", 6 | "keywords": [ 7 | "serverless", 8 | "loopback", 9 | "lambda", 10 | "aws" 11 | ], 12 | "license": "MIT", 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/botbits/serverless-loopback.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/botbits/serverless-loopback/issues" 19 | }, 20 | "homepage": "https://github.com/botbits/serverless-loopback#readme", 21 | "engines": { 22 | "node": ">=6" 23 | }, 24 | "main": "lambda-wrapper.js", 25 | "scripts": { 26 | "lint": "eslint .", 27 | "lb": "node_modules/loopback-cli/bin/loopback-cli.js", 28 | "lb-model": "node_modules/loopback-cli/bin/loopback-cli.js model", 29 | "sls-deploy": "node_modules/serverless/bin/serverless deploy --verbose", 30 | "sls-cleanup": "node_modules/serverless/bin/serverless remove --verbose", 31 | "snyk-protect": "snyk protect", 32 | "prepare": "npm run snyk-protect" 33 | }, 34 | "dependencies": { 35 | "compression": "^1.0.3", 36 | "cors": "^2.5.2", 37 | "helmet": "^3.21.1", 38 | "loopback": "^3.28.0", 39 | "loopback-boot": "^2.27.1", 40 | "loopback-component-explorer": "^6.3.1", 41 | "loopback-connector-mysql": "^5.3.1", 42 | "serve-favicon": "^2.0.1", 43 | "serverless-http": "^1.8.0", 44 | "strong-error-handler": "^3.2.0", 45 | "snyk": "^1.1111.0" 46 | }, 47 | "devDependencies": { 48 | "eslint": "^5.9.0", 49 | "eslint-config-loopback": "^12.0.0", 50 | "loopback-cli": "^5.2.0", 51 | "serverless": "^1.32.0" 52 | }, 53 | "snyk": true 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-loopback 2 | [![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com) 3 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/botbits/serverless-loopback/master/LICENSE) 4 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3988cecd1c154bb99c310fb77bbc1ae9)](https://www.codacy.com/app/marcelobern/serverless-loopback?utm_source=github.com&utm_medium=referral&utm_content=botbits/serverless-loopback&utm_campaign=Badge_Grade) 5 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fbotbits%2Fserverless-loopback.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fbotbits%2Fserverless-loopback?ref=badge_shield) 6 | 7 | A collection of sample [loopback](https://loopback.io/) applications deployed to [AWS Lambda](https://aws.amazon.com/lambda/) using [serverless](https://serverless.com/). 8 | 9 | ## Overview 10 | 11 | The following examples are available: 12 | 13 | - [Loopback 3 (using express)](examples/lb3-express/README.md) 14 | - [Loopback 4 (via express)](examples/lb4-express/README.md) 15 | 16 | ## Using 17 | 18 | [Create a *serverless* service](https://serverless.com/framework/docs/providers/aws/cli-reference/create) from this template by using the command: 19 | 20 | `serverless create --template-url https://github.com/botbits/serverless-loopback/examples/` 21 | 22 | Or clone the git repo: 23 | 24 | `git clone https://github.com/botbits/serverless-loopback` 25 | 26 | and `cd examples` to start checking your favorite example! 27 | 28 | ## Contributing 29 | 30 | Please feel free to contribute your examples of using loopback4 with other web frameworks under the `examples` folder. 31 | 32 | ## License 33 | 34 | MIT © [BotBitsSM](https://github.com/botbits) 35 | -------------------------------------------------------------------------------- /examples/lb4-express/serverless.yml: -------------------------------------------------------------------------------- 1 | service: serverless-loopback4-express 2 | frameWorkVersion: ">=1.72.0 <2.0.0" 3 | 4 | plugins: 5 | - serverless-offline 6 | 7 | provider: 8 | name: aws 9 | runtime: nodejs12.x 10 | stage: ${opt:stage, self:custom.defaultStage} 11 | region: us-east-1 12 | logs: 13 | restApi: true 14 | 15 | custom: 16 | serverless-offline: 17 | httpPort: 5000 18 | defaultStage: dev 19 | 20 | package: 21 | exclude: 22 | - .vscode/** 23 | - data/** 24 | - dist/__tests__/** 25 | - src/** 26 | - tsconfig.tsbuildinfo 27 | 28 | functions: 29 | loopback: 30 | handler: dist/lambda-wrapper.handler 31 | name: '${self:provider.stage}-loopback4-express' 32 | description: Sample loopback4 (via express) running in AWS lambda 33 | memorySize: 512 34 | timeout: 60 35 | events: 36 | - http: ANY / 37 | - http: ANY {proxy+} 38 | 39 | resources: 40 | # The "Outputs" that your AWS CloudFormation Stack should produce. This allows references between services. 41 | Outputs: 42 | LoopbackPostman: 43 | Description: The baseUrl value to configure for your Postman collection 44 | Value: 45 | 'Fn::Join': 46 | - '' 47 | - - 'https://' 48 | - Ref: 'ApiGatewayRestApi' 49 | - '.execute-api.' 50 | - Ref: 'AWS::Region' 51 | - '.' 52 | - Ref: 'AWS::URLSuffix' 53 | - '/${self:provider.stage}/api' 54 | LoopbackApiExplorer: 55 | Description: The URL to directly access the loopback API explorer 56 | Value: 57 | 'Fn::Join': 58 | - '' 59 | - - 'https://' 60 | - Ref: 'ApiGatewayRestApi' 61 | - '.execute-api.' 62 | - Ref: 'AWS::Region' 63 | - '.' 64 | - Ref: 'AWS::URLSuffix' 65 | - '/${self:provider.stage}/api/explorer/' 66 | -------------------------------------------------------------------------------- /examples/lb4-express/src/server.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {once} from 'events'; 7 | import express, {Request, Response} from 'express'; 8 | import http from 'http'; 9 | import path from 'path'; 10 | import {ApplicationConfig, NoteApplication} from './application'; 11 | 12 | export {ApplicationConfig}; 13 | 14 | export class ExpressServer { 15 | public readonly app: express.Application; 16 | public readonly lbApp: NoteApplication; 17 | private server?: http.Server; 18 | 19 | constructor(options: ApplicationConfig = {}) { 20 | this.app = express(); 21 | this.lbApp = new NoteApplication(options); 22 | 23 | // Expose the front-end assets via Express, not as LB4 route 24 | this.app.use('/api', this.lbApp.requestHandler); 25 | 26 | // Custom Express routes 27 | this.app.get('/', function (_req: Request, res: Response) { 28 | res.sendFile(path.join(__dirname, '../public/express.html')); 29 | }); 30 | this.app.get('/hello', function (_req: Request, res: Response) { 31 | res.send('Hello world!'); 32 | }); 33 | 34 | // Serve static files in the public folder 35 | this.app.use(express.static(path.join(__dirname, '../public'))); 36 | } 37 | 38 | public async boot() { 39 | await this.lbApp.boot(); 40 | } 41 | 42 | public async start() { 43 | await this.lbApp.start(); 44 | const port = this.lbApp.restServer.config.port ?? 3000; 45 | const host = this.lbApp.restServer.config.host ?? '127.0.0.1'; 46 | this.server = this.app.listen(port, host); 47 | await once(this.server, 'listening'); 48 | } 49 | 50 | // For testing purposes 51 | public async stop() { 52 | if (!this.server) return; 53 | await this.lbApp.stop(); 54 | this.server.close(); 55 | await once(this.server, 'close'); 56 | this.server = undefined; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/lb4-express/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LoopBack 4 REST API on Express 5 | 6 | 7 | 8 | 9 | 14 | 15 | 61 | 62 | 63 | 64 |
65 |

express-composition

66 |

Version 1.0.0

67 | 68 |

OpenAPI spec: /openapi.json

69 |

API Explorer: /explorer

70 |

Notes: /notes

71 |
72 | 73 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /examples/lb4-express/src/__tests__/acceptance/express.acceptance.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {Client, expect} from '@loopback/testlab'; 7 | import {HelloObserver} from '../../observers'; 8 | import {ExpressServer} from '../../server'; 9 | import {setupExpressApplication} from './test-helper'; 10 | 11 | describe('ExpressApplication', () => { 12 | let server: ExpressServer; 13 | let client: Client; 14 | 15 | before('setupApplication', async () => { 16 | ({server, client} = await setupExpressApplication()); 17 | }); 18 | 19 | after('closes application', async () => { 20 | await server.stop(); 21 | }); 22 | 23 | it('displays front page', async () => { 24 | await client 25 | .get('/') 26 | .expect(200) 27 | .expect('Content-Type', /text\/html/); 28 | }); 29 | 30 | it('displays a static page', async () => { 31 | await client 32 | .get('/notes.html') 33 | .expect(200) 34 | .expect('Content-Type', /text\/html/) 35 | .expect(/

Notes/); 36 | }); 37 | 38 | it('gets hello world', async () => { 39 | await client.get('/hello').expect(200).expect('Hello world!'); 40 | }); 41 | 42 | it('redirects to "api/explorer" from "api/explorer"', async () => { 43 | await client 44 | .get('/api/explorer') 45 | .expect(301) 46 | // expect relative redirect so that it works seamlessly with many forms 47 | // of base path, whether within the app or applied by a reverse proxy 48 | .expect('location', './explorer/'); 49 | }); 50 | 51 | it('displays explorer page', async () => { 52 | await client 53 | .get('/api/explorer/') 54 | .expect(200) 55 | .expect('content-type', /html/) 56 | .expect(/url\: '\.\/openapi\.json'\,/) 57 | .expect(/LoopBack API Explorer/); 58 | }); 59 | 60 | it('triggers life cycle start', async () => { 61 | const observer: HelloObserver = await server.lbApp.get( 62 | 'lifeCycleObservers.HelloObserver', 63 | ); 64 | expect(observer.events.length).to.be.above(0); 65 | expect(observer.events[0]).to.match(/hello-start$/); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /examples/lb4-express/src/__tests__/acceptance/note.acceptance.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import {Client, expect} from '@loopback/testlab'; 7 | import {setupExpressApplication, givenNote} from './test-helper'; 8 | import {NoteApplication} from '../../application'; 9 | import {NoteRepository} from '../../repositories'; 10 | import {ExpressServer} from '../../server'; 11 | 12 | describe('NoteApplication', () => { 13 | let server: ExpressServer; 14 | let client: Client; 15 | let lbApp: NoteApplication; 16 | let noteRepo: NoteRepository; 17 | 18 | before('setupApplication', async () => { 19 | ({server, client, lbApp} = await setupExpressApplication()); 20 | await changeDataSourceConfig(); 21 | await givenNoteRepository(); 22 | }); 23 | 24 | beforeEach(async () => { 25 | await noteRepo.deleteAll(); 26 | }); 27 | 28 | after('closes application', async () => { 29 | await server.stop(); 30 | }); 31 | 32 | it('creates a note', async function () { 33 | const note = givenNote(); 34 | const response = await client.post('/api/notes').send(note).expect(200); 35 | expect(response.body).to.containDeep(note); 36 | const result = await noteRepo.findById(response.body.id); 37 | expect(result).to.containDeep(note); 38 | }); 39 | 40 | it('gets notes', async () => { 41 | const note = givenNote(); 42 | let response = await client.get('/api/notes').expect(200); 43 | expect(response.body).to.be.empty(); 44 | await client.post('/api/notes').send(note).expect(200); 45 | response = await client.get('/api/notes').expect(200); 46 | expect(response.body).to.not.be.empty(); 47 | }); 48 | 49 | it('displays static front page from Note app', async () => { 50 | await client 51 | .get('/api/') 52 | .expect(200) 53 | .expect('Content-Type', /text\/html/) 54 | .expect(/<h1>express-composition/); 55 | }); 56 | 57 | async function givenNoteRepository() { 58 | noteRepo = await lbApp.getRepository(NoteRepository); 59 | } 60 | 61 | async function changeDataSourceConfig() { 62 | /** 63 | * Override default config for DataSource for testing so we don't write 64 | * test data to file when using the memory connector. 65 | */ 66 | lbApp.bind('datasources.config.ds').to({ 67 | name: 'ds', 68 | connector: 'memory', 69 | }); 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /examples/lb4-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@loopback/example-express-composition", 3 | "version": "2.2.3", 4 | "description": "LoopBack 4 REST API on Express", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "keywords": [ 8 | "loopback-application", 9 | "loopback", 10 | "example", 11 | "tutorial", 12 | "express" 13 | ], 14 | "engines": { 15 | "node": ">=10" 16 | }, 17 | "scripts": { 18 | "presls-deploy": "npm run build", 19 | "sls-deploy": "serverless deploy --verbose", 20 | "sls-cleanup": "serverless remove --verbose", 21 | "presls-offline": "npm run build", 22 | "sls-offline": "serverless offline", 23 | "build": "lb-tsc", 24 | "build:watch": "lb-tsc --watch", 25 | "clean": "lb-clean *example-express-composition*.tgz dist *.tsbuildinfo package", 26 | "lint": "npm run prettier:check && npm run eslint", 27 | "lint:fix": "npm run eslint:fix && npm run prettier:fix", 28 | "prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"", 29 | "prettier:check": "npm run prettier:cli -- -l", 30 | "prettier:fix": "npm run prettier:cli -- --write", 31 | "eslint": "lb-eslint --report-unused-disable-directives .", 32 | "eslint:fix": "npm run eslint -- --fix", 33 | "pretest": "npm run clean && npm run build", 34 | "test": "lb-mocha \"dist/__tests__/**/*.js\"", 35 | "posttest": "npm run lint", 36 | "test:dev": "lb-mocha --allow-console-logs dist/__tests__/**/*.js && npm run posttest", 37 | "migrate": "node ./dist/migrate", 38 | "prestart": "npm run build", 39 | "start": "node ." 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "https://github.com/strongloop/loopback-next.git", 44 | "directory": "examples/express-composition" 45 | }, 46 | "author": "IBM Corp.", 47 | "copyright.owner": "IBM Corp.", 48 | "license": "MIT", 49 | "publishConfig": { 50 | "access": "public" 51 | }, 52 | "dependencies": { 53 | "@loopback/boot": "^2.3.3", 54 | "@loopback/core": "^2.8.0", 55 | "@loopback/openapi-v3": "^3.4.3", 56 | "@loopback/repository": "^2.7.0", 57 | "@loopback/rest": "^5.1.1", 58 | "@loopback/rest-explorer": "^5.0.8", 59 | "@loopback/service-proxy": "^2.3.2", 60 | "express": "^4.17.1", 61 | "serverless-http": "^2.5.0", 62 | "tslib": "^2.0.0" 63 | }, 64 | "devDependencies": { 65 | "@loopback/build": "^9.0.8", 66 | "@loopback/cli": "^2.9.0", 67 | "@loopback/eslint-config": "^8.0.1", 68 | "@loopback/testlab": "^3.1.7", 69 | "@types/express": "^4.17.6", 70 | "@types/node": "^10.17.26", 71 | "eslint": "^7.2.0", 72 | "serverless": "^3.28.1", 73 | "serverless-offline": "^12.0.4", 74 | "typescript": "~3.9.5" 75 | }, 76 | "gitHead": "f31b7e6de5a405a015cdd774f63d699b35d943cc" 77 | } 78 | -------------------------------------------------------------------------------- /examples/lb4-express/src/controllers/note.controller.ts: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2019,2020. All Rights Reserved. 2 | // Node module: @loopback/example-express-composition 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | import { 7 | Count, 8 | CountSchema, 9 | Filter, 10 | repository, 11 | Where, 12 | } from '@loopback/repository'; 13 | import { 14 | del, 15 | get, 16 | getModelSchemaRef, 17 | param, 18 | patch, 19 | post, 20 | put, 21 | requestBody, 22 | } from '@loopback/rest'; 23 | import {Note} from '../models'; 24 | import {NoteRepository} from '../repositories'; 25 | 26 | export class NoteController { 27 | constructor( 28 | @repository(NoteRepository) 29 | public noteRepository: NoteRepository, 30 | ) {} 31 | 32 | @post('/notes', { 33 | responses: { 34 | '200': { 35 | description: 'Note model instance', 36 | content: {'application/json': {schema: getModelSchemaRef(Note)}}, 37 | }, 38 | }, 39 | }) 40 | async create( 41 | @requestBody({ 42 | content: { 43 | 'application/json': { 44 | schema: getModelSchemaRef(Note, {title: 'NewNote', exclude: ['id']}), 45 | }, 46 | }, 47 | }) 48 | note: Omit<Note, 'id'>, 49 | ): Promise<Note> { 50 | return this.noteRepository.create(note); 51 | } 52 | 53 | @get('/notes/count', { 54 | responses: { 55 | '200': { 56 | description: 'Note model count', 57 | content: {'application/json': {schema: CountSchema}}, 58 | }, 59 | }, 60 | }) 61 | async count(@param.where(Note) where?: Where<Note>): Promise<Count> { 62 | return this.noteRepository.count(where); 63 | } 64 | 65 | @get('/notes', { 66 | responses: { 67 | '200': { 68 | description: 'Array of Note model instances', 69 | content: { 70 | 'application/json': { 71 | schema: {type: 'array', items: getModelSchemaRef(Note)}, 72 | }, 73 | }, 74 | }, 75 | }, 76 | }) 77 | async find( 78 | @param.filter(Note) 79 | filter?: Filter<Note>, 80 | ): Promise<Note[]> { 81 | return this.noteRepository.find(filter); 82 | } 83 | 84 | @patch('/notes', { 85 | responses: { 86 | '200': { 87 | description: 'Note PATCH success count', 88 | content: {'application/json': {schema: CountSchema}}, 89 | }, 90 | }, 91 | }) 92 | async updateAll( 93 | @requestBody({ 94 | content: { 95 | 'application/json': { 96 | schema: getModelSchemaRef(Note, {partial: true}), 97 | }, 98 | }, 99 | }) 100 | note: Partial<Note>, 101 | @param.where(Note) where?: Where<Note>, 102 | ): Promise<Count> { 103 | return this.noteRepository.updateAll(note, where); 104 | } 105 | 106 | @get('/notes/{id}', { 107 | responses: { 108 | '200': { 109 | description: 'Note model instance', 110 | content: {'application/json': {schema: getModelSchemaRef(Note)}}, 111 | }, 112 | }, 113 | }) 114 | async findById(@param.path.number('id') id: number): Promise<Note> { 115 | return this.noteRepository.findById(id); 116 | } 117 | 118 | @patch('/notes/{id}', { 119 | responses: { 120 | '204': { 121 | description: 'Note PATCH success', 122 | }, 123 | }, 124 | }) 125 | async updateById( 126 | @param.path.number('id') id: number, 127 | @requestBody({ 128 | content: { 129 | 'application/json': { 130 | schema: getModelSchemaRef(Note, {partial: true}), 131 | }, 132 | }, 133 | }) 134 | note: Partial<Note>, 135 | ): Promise<void> { 136 | await this.noteRepository.updateById(id, note); 137 | } 138 | 139 | @put('/notes/{id}', { 140 | responses: { 141 | '204': { 142 | description: 'Note PUT success', 143 | }, 144 | }, 145 | }) 146 | async replaceById( 147 | @param.path.number('id') id: number, 148 | @requestBody() note: Note, 149 | ): Promise<void> { 150 | await this.noteRepository.replaceById(id, note); 151 | } 152 | 153 | @del('/notes/{id}', { 154 | responses: { 155 | '204': { 156 | description: 'Note DELETE success', 157 | }, 158 | }, 159 | }) 160 | async deleteById(@param.path.number('id') id: number): Promise<void> { 161 | await this.noteRepository.deleteById(id); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /examples/lb3-express/README.md: -------------------------------------------------------------------------------- 1 | # examples/lb3-express 2 | 3 | This example shows how to use the [serverless framework](https://www.serverless.com/framework/docs/providers/aws/) to run loopback3 (with express) in [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html). 4 | 5 | ## Overview 6 | 7 | A RDS (MySQL) database is created, along with all necessary virtual AWS infrastructure (VPC, subnets, DBSubnetGroup) to connect a lambda function running loopback to the MySQL database. The MySQL connection parameters are retrieved from lambda environment variables. 8 | <!-- 9 | For ease of management RDS (MySQL) username/password can be retrieved from [AWS SSM Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) and used in `serverless.yml` [see section *Handling Secrets for Small Teams & Projects*]](https://serverless.com/blog/serverless-secrets-api-keys/). --> 10 | 11 | Check out [this article](https://medium.com/smac-4u/serverless-loopback-9ff0d6fa539d) for a more in-depth explanation of this sample. 12 | 13 | ## About Sample Provided 14 | 15 | The sample loopback application provided was created by following the process below: 16 | 17 | 1. Using the command `npm run lb` and selecting: 18 | 19 | - What kind of application do you have in mind? `api-server (A LoopBack API server with local User auth)` 20 | 21 | 2. Adding a new MySQL database to `lib/server/datasources.json`. 22 | 23 | 3. Adding the CoffeeShop model and initializing it with data: 24 | 25 | - `lib/common/coffee-shop.json`: CoffeeShop model definition 26 | - `lib/server/model-config.json`: add CoffeeShop model so it can be loaded 27 | - `lib/server/boot/create-sample-models.js`: initialize CoffeeShop model with data 28 | 29 | ## Customizing & Deploying This Sample 30 | 31 | The following steps can be used to customize this sample to your needs and then deploy: 32 | 33 | <!-- 0. (*optional*) For ease of management add MySQL username/password to the AWS SSM Parameter Store (using [AWS Console(https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-console.html)] or [AWS CLI(https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html)]). 34 | --> 35 | 1. Create your own loopback models with the command `npm run lb-model` 36 | 37 | 2. The RDS `mySqlDb` in `serverless.yml` is configured to be as low cost as possible (not suitable for production!) so feel free to [customize it to your needs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html). 38 | 39 | 3. Deploy your project to AWS using the command `npm run sls-deploy` 40 | 41 | 4. From the serverless `Stack Outputs`, retrieve `LoopbackApiExplorer` to access the loopback4 API explorer (it should look something like `https://API_GATEWAY_ID.execute-api.AWS_REGION.amazonaws.com/SERVERLESS_STAGE/api/explorer/`). You should end up with an URL similar to `https://XXXXXXXXXX.execute-api.us-east-1.amazonaws.com/dev/api/explorer/`. 42 | 43 | *Note*: An error might be observed the first time lambda runs after (re-)creating the RDS database as the CoffeeTable model table might not be initialized before your API is invoked. This error would look something like: 44 | 45 | ```json 46 | { 47 | "error": { 48 | "statusCode": 500, 49 | "name": "Error", 50 | "message": "ER_NO_SUCH_TABLE: Table 'MY_TEST_DB.CoffeeShop' doesn't exist", 51 | "code": "ER_NO_SUCH_TABLE", 52 | "errno": 1146, 53 | "sqlMessage": "Table 'MY_TEST_DB.CoffeeShop' doesn't exist", 54 | "sqlState": "42S02", 55 | "index": 0, 56 | "sql": "SELECT count(*) as \"cnt\" FROM `CoffeeShop` ", 57 | "stack": "Error: ER_NO_SUCH_TABLE: Table 'MY_TEST_DB.CoffeeShop' doesn't exist\n at ..." 58 | } 59 | } 60 | ``` 61 | 62 | Retry after a few seconds and it all should work. 63 | 64 | ## Cleaning Up The Sample 65 | 66 | Once you are done with the sample environment, avoid unnecessary AWS charges by removing your serverless deployment with the command `npm run sls-cleanup`. 67 | 68 | If you run into a cleanup [error similar to the one below](https://forum.serverless.com/t/very-long-delay-when-doing-sls-remove-of-lambda-in-a-vpc/2535), you will need to manually remove the CloudFormation stack by going to: <https://console.aws.amazon.com/cloudformation> or using the [aws-cli](https://aws.amazon.com/cli/). 69 | 70 | ```shell 71 | Serverless Error --------------------------------------- 72 | 73 | An error occurred: mySubnet2 - The subnet 'subnet-077e0f72824fe5dd3' has dependencies and cannot be deleted. (Service: AmazonEC2; Status Code: 400; Error Code: DependencyViolation; Request ID: XXX). 74 | ``` 75 | 76 | ## License 77 | 78 | MIT © [BotBits<sup>SM</sup>](https://github.com/botbits) 79 | -------------------------------------------------------------------------------- /examples/lb3-express/serverless.yml: -------------------------------------------------------------------------------- 1 | service: serverless-loopback 2 | provider: 3 | name: aws 4 | runtime: nodejs8.10 5 | stage: ${opt:stage, self:custom.defaultStage} 6 | region: us-east-1 7 | 8 | custom: 9 | defaultStage: dev 10 | DB: 11 | dev: 12 | # HOST: MY_HOST # provide this information if DB already exists 13 | # PORT: MY_PORT # provide this information if DB already exists 14 | NAME: MY_TEST_DB 15 | USERNAME: admin 16 | PASSWORD: password 17 | 18 | functions: 19 | loopback: 20 | handler: lambda-wrapper.handler 21 | name: '${self:provider.stage}-loopback' 22 | description: Sample loopback running in AWS lambda 23 | memorySize: 512 24 | timeout: 60 25 | events: 26 | - http: ANY / 27 | - http: ANY {proxy+} 28 | environment: 29 | DB_HOST: { 'Fn::GetAtt': ['mySqlDb', 'Endpoint.Address' ] } 30 | DB_PORT: { 'Fn::GetAtt': ['mySqlDb', 'Endpoint.Port' ] } 31 | DB_NAME: '${self:custom.DB.${self:provider.stage}.NAME}' 32 | DB_USERNAME: '${self:custom.DB.${self:provider.stage}.USERNAME}' # this will show in clear text in CloudFormation 33 | DB_PASSWORD: '${self:custom.DB.${self:provider.stage}.PASSWORD}' # this will show in clear text in CloudFormation 34 | vpc: 35 | securityGroupIds: 36 | - { 'Fn::GetAtt' : ['myVPC', 'DefaultSecurityGroup'] } 37 | subnetIds: 38 | - Ref: mySubnet1 39 | - Ref: mySubnet2 40 | 41 | resources: 42 | Resources: 43 | # https://serverless.com/framework/docs/providers/aws/guide/resources#aws-cloudformation-resource-reference 44 | # uses CloudFormation syntax - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html 45 | myVPC: 46 | Type: AWS::EC2::VPC 47 | Properties: 48 | CidrBlock: 172.31.0.0/16 49 | Tags: 50 | - Key: Name 51 | Value: ${self:service}-vpc 52 | 53 | mySubnet1: 54 | Type: AWS::EC2::Subnet 55 | Properties: 56 | AvailabilityZone: ${self:provider.region}a 57 | VpcId: 58 | Ref: myVPC 59 | CidrBlock: 172.31.0.0/20 60 | 61 | mySubnet2: 62 | Type: AWS::EC2::Subnet 63 | Properties: 64 | AvailabilityZone: ${self:provider.region}b 65 | VpcId: 66 | Ref: myVPC 67 | CidrBlock: 172.31.16.0/20 68 | 69 | myDBSubnetGroup: 70 | Type: 'AWS::RDS::DBSubnetGroup' 71 | Properties: 72 | DBSubnetGroupDescription: 'description' 73 | SubnetIds: 74 | - Ref: mySubnet1 75 | - Ref: mySubnet2 76 | 77 | mySqlDb: 78 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html 79 | Type: AWS::RDS::DBInstance 80 | Properties: 81 | AllocatedStorage: '5' # minimal size for this sample 82 | BackupRetentionPeriod: 0 # disabling backups for this sample 83 | DBInstanceClass: 'db.t2.micro' # minimal size for this sample 84 | DBName: '${self:custom.DB.${self:provider.stage}.NAME}' # creating a database for this sample 85 | DBSubnetGroupName: { 'Ref' : 'myDBSubnetGroup' } 86 | Engine: 'mysql' 87 | MasterUsername: '${self:custom.DB.${self:provider.stage}.USERNAME}' # this will show in clear text in CloudFormation 88 | MasterUserPassword: '${self:custom.DB.${self:provider.stage}.PASSWORD}' # this will show in clear text in CloudFormation 89 | MultiAZ: false # disabling for this sample 90 | PubliclyAccessible: false 91 | VPCSecurityGroups: 92 | - { 'Fn::GetAtt' : ['myVPC', 'DefaultSecurityGroup'] } 93 | 94 | # The "Outputs" that your AWS CloudFormation Stack should produce. This allows references between services. 95 | Outputs: 96 | DbInstanceIdentifier: 97 | Description: The DB Name for the (MySQL) RDS created 98 | Value: 99 | Ref: mySqlDb 100 | LoopbackPostman: 101 | Description: The baseUrl value to configure for your Postman collection 102 | Value: 103 | 'Fn::Join': 104 | - '' 105 | - - 'https://' 106 | - Ref: 'ApiGatewayRestApi' 107 | - '.execute-api.' 108 | - Ref: 'AWS::Region' 109 | - '.' 110 | - Ref: 'AWS::URLSuffix' 111 | - '/${self:provider.stage}/api' 112 | LoopbackApiExplorer: 113 | Description: The URL to directly access the loopback API explorer 114 | Value: 115 | 'Fn::Join': 116 | - '' 117 | - - 'https://' 118 | - Ref: 'ApiGatewayRestApi' 119 | - '.execute-api.' 120 | - Ref: 'AWS::Region' 121 | - '.' 122 | - Ref: 'AWS::URLSuffix' 123 | - '/${self:provider.stage}/api/explorer/' 124 | -------------------------------------------------------------------------------- /examples/lb3-express/.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-450202: 7 | - loopback > loopback-datasource-juggler > async > lodash: 8 | patched: '2019-07-06T05:03:14.104Z' 9 | - loopback-connector-mysql > lodash: 10 | patched: '2019-07-06T05:03:14.104Z' 11 | - loopback-component-explorer > lodash: 12 | patched: '2019-07-06T05:03:14.104Z' 13 | - loopback-component-explorer > loopback-swagger > lodash: 14 | patched: '2019-07-06T05:03:14.104Z' 15 | - strong-error-handler > strong-globalize > lodash: 16 | patched: '2019-07-06T05:03:14.104Z' 17 | - loopback-boot > strong-globalize > lodash: 18 | patched: '2019-07-06T05:03:14.104Z' 19 | - loopback > async > lodash: 20 | patched: '2019-07-06T05:03:14.104Z' 21 | - loopback-connector-mysql > async > lodash: 22 | patched: '2019-07-06T05:03:14.104Z' 23 | - loopback-connector-mysql > strong-globalize > lodash: 24 | patched: '2019-07-06T05:03:14.104Z' 25 | - loopback-component-explorer > strong-globalize > lodash: 26 | patched: '2019-07-06T05:03:14.104Z' 27 | - loopback > strong-globalize > lodash: 28 | patched: '2019-07-06T05:03:14.104Z' 29 | - loopback > loopback-datasource-juggler > lodash: 30 | patched: '2019-07-06T05:03:14.104Z' 31 | - loopback-component-explorer > loopback-swagger > async > lodash: 32 | patched: '2019-07-06T05:03:14.104Z' 33 | - loopback > loopback-phase > strong-globalize > lodash: 34 | patched: '2019-07-06T05:03:14.104Z' 35 | - loopback > strong-remoting > jayson > lodash: 36 | patched: '2019-07-06T05:03:14.104Z' 37 | - loopback > loopback-connector-remote > loopback-datasource-juggler > lodash: 38 | patched: '2019-07-06T05:03:14.104Z' 39 | - loopback-connector-mysql > loopback-connector > strong-globalize > lodash: 40 | patched: '2019-07-06T05:03:14.104Z' 41 | - loopback > strong-remoting > async > lodash: 42 | patched: '2019-07-06T05:03:14.104Z' 43 | - loopback-connector-mysql > loopback-connector > async > lodash: 44 | patched: '2019-07-06T05:03:14.104Z' 45 | - loopback-component-explorer > loopback-swagger > strong-globalize > lodash: 46 | patched: '2019-07-06T05:03:14.104Z' 47 | - loopback-boot > lodash: 48 | patched: '2019-07-06T05:03:14.104Z' 49 | - loopback > loopback-phase > async > lodash: 50 | patched: '2019-07-06T05:03:14.104Z' 51 | - loopback > loopback-datasource-juggler > strong-globalize > lodash: 52 | patched: '2019-07-06T05:03:14.104Z' 53 | - loopback > strong-remoting > strong-globalize > lodash: 54 | patched: '2019-07-06T05:03:14.104Z' 55 | - loopback > loopback-connector-remote > strong-remoting > jayson > lodash: 56 | patched: '2019-07-06T05:03:14.104Z' 57 | - loopback > loopback-connector-remote > strong-remoting > strong-globalize > lodash: 58 | patched: '2019-07-06T05:03:14.104Z' 59 | - loopback > loopback-connector-remote > strong-remoting > async > lodash: 60 | patched: '2019-07-06T05:03:14.104Z' 61 | - loopback > strong-remoting > loopback-phase > async > lodash: 62 | patched: '2019-07-06T05:03:14.104Z' 63 | - loopback > strong-remoting > loopback-phase > strong-globalize > lodash: 64 | patched: '2019-07-06T05:03:14.104Z' 65 | - loopback > loopback-datasource-juggler > loopback-connector > async > lodash: 66 | patched: '2019-07-06T05:03:14.104Z' 67 | - loopback > loopback-connector-remote > loopback-datasource-juggler > strong-globalize > lodash: 68 | patched: '2019-07-06T05:03:14.104Z' 69 | - loopback > loopback-datasource-juggler > loopback-connector > strong-globalize > lodash: 70 | patched: '2019-07-06T05:03:14.104Z' 71 | - loopback > strong-remoting > strong-error-handler > strong-globalize > lodash: 72 | patched: '2019-07-06T05:03:14.104Z' 73 | - loopback > loopback-connector-remote > loopback-datasource-juggler > async > lodash: 74 | patched: '2019-07-06T05:03:14.104Z' 75 | - loopback-boot > strong-globalize > g11n-pipeline > swagger-client > lodash: 76 | patched: '2019-07-06T05:03:14.104Z' 77 | - loopback > loopback-connector-remote > loopback-datasource-juggler > loopback-connector > strong-globalize > lodash: 78 | patched: '2019-07-06T05:03:14.104Z' 79 | - loopback > loopback-connector-remote > strong-remoting > loopback-phase > strong-globalize > lodash: 80 | patched: '2019-07-06T05:03:14.104Z' 81 | - loopback > loopback-connector-remote > loopback-datasource-juggler > loopback-connector > async > lodash: 82 | patched: '2019-07-06T05:03:14.104Z' 83 | - loopback > loopback-connector-remote > strong-remoting > strong-error-handler > strong-globalize > lodash: 84 | patched: '2019-07-06T05:03:14.104Z' 85 | - loopback > loopback-connector-remote > strong-remoting > loopback-phase > async > lodash: 86 | patched: '2019-07-06T05:03:14.104Z' 87 | - loopback-boot > strong-globalize > g11n-pipeline > swagger-client > isomorphic-form-data > form-data > async > lodash: 88 | patched: '2019-07-06T05:03:14.104Z' 89 | SNYK-JS-HTTPSPROXYAGENT-469131: 90 | - snyk > proxy-agent > https-proxy-agent: 91 | patched: '2019-10-05T05:08:23.949Z' 92 | - snyk > proxy-agent > pac-proxy-agent > https-proxy-agent: 93 | patched: '2019-10-05T05:08:23.949Z' 94 | -------------------------------------------------------------------------------- /examples/lb4-express/README.md: -------------------------------------------------------------------------------- 1 | # examples/lb4-express 2 | 3 | This example shows how to use the [serverless framework](https://www.serverless.com/framework/docs/providers/aws/) to run loopback4 (with express) in [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html). 4 | 5 | ## Overview 6 | 7 | A RDS (MySQL) database is created, along with all necessary virtual AWS infrastructure (VPC, subnets, DBSubnetGroup) to connect a lambda function running loopback4 to the MySQL database. The MySQL connection parameters are retrieved from lambda environment variables. 8 | <!-- 9 | For ease of management RDS (MySQL) username/password can be retrieved from [AWS SSM Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) and used in `serverless.yml` [see section *Handling Secrets for Small Teams & Projects*]](https://serverless.com/blog/serverless-secrets-api-keys/). --> 10 | 11 | <!-- 12 | Check out [this article](https://medium.com/smac-4u/serverless-loopback-9ff0d6fa539d) for a more in-depth explanation of this sample. 13 | --> 14 | 15 | ## About Sample Provided 16 | 17 | The sample loopback4 application provided was created by following the process below: 18 | 19 | 1. Create an Express Application with LoopBack REST API by following [this tutorial](https://loopback.io/doc/en/lb4/express-with-lb4-rest-tutorial.html). We followed the shortcut by running the command `lb4 example express-composition`. 20 | 21 | 2. Remove the `file` parameter from the [memory datasource](src/datasources/ds.datasource.ts) to avoid lambda errors. 22 | 23 | 3. Create the following files required to deploying it all to AWS Lambda using the [serverless framework](https://www.serverless.com/framework/docs/providers/aws/): 24 | 25 | - [`src/lambda-wrapper.js`](src/lambda-wrapper.js): this file uses [`serverless-http`](https://www.npmjs.com/package/serverless-http) to wrap the loopback4 application via [express](https://www.npmjs.com/package/express). 26 | 27 | - [`serverless.yml`](serverless.yml): our [*serverless* service definition](https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/). Here is where we create all AWS resources (including Lambda, and API Gateway). 28 | 29 | 4. Update your `tsconfig.json` file to include `"allowJs": true` in your `compilerOptions` section to ensire the [`src/lambda-wrapper.js`](src/lambda-wrapper.js) file is included in the loopback4 build. 30 | 31 | 5. Install the `serverless-http` module and its dependencies with command `npm install serverless-http`. 32 | 33 | 6. Install the `serverless` module and its dependencies with command `npm install -D serverless`. 34 | 35 | 7. As we planed to troubleshoot locally, we installed the `serverless-offline` module and its dependencies with command `npm install -D serverless-offline`. 36 | 37 | 8. In order to ensure your loopback4 application is built prior to deploying it to AWS Lambda or running it locally we included the following scripts in the `package.json` file: 38 | 39 | ``` 40 | "presls-deploy": "npm run build", 41 | "sls-deploy": "serverless deploy --verbose", 42 | "sls-cleanup": "serverless remove --verbose", 43 | "presls-offline": "npm run build", 44 | "sls-offline": "serverless offline", 45 | ``` 46 | 47 | Please note that `serverless-http` also supports [other web frameworks](https://www.npmjs.com/package/serverless-http#supported-frameworks). The loopback support indicated is for [`@loopback-rest`](https://www.npmjs.com/package/@loopback/rest). Please feel free to contribute your examples of using loopback4 with other web frameworks under the `examples` folder. 48 | 49 | ## Customizing & Deploying This Sample 50 | 51 | The following steps can be used to customize this sample to your needs and then deploy: 52 | 53 | <!-- 0. (*optional*) For ease of management add MySQL username/password to the AWS SSM Parameter Store (using [AWS Console(https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-console.html)] or [AWS CLI(https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html)]). 54 | --> 55 | 1. Create your own loopback models, datasources, repositories, and controllers. 56 | 57 | 2. Deploy your project to AWS using the command `npm run sls-deploy` 58 | 59 | 3. From the serverless `Stack Outputs`, retrieve `LoopbackApiExplorer` to access the loopback4 API explorer (it should look something like `https://API_GATEWAY_ID.execute-api.AWS_REGION.amazonaws.com/SERVERLESS_STAGE/api/explorer/`). You should end up with an URL similar to `https://XXXXXXXXXX.execute-api.us-east-1.amazonaws.com/dev/api/explorer/`. 60 | 61 | 4. From the lopback4 API explorer download the `openapi.json` file and [import it into Postman](https://learning.postman.com/docs/postman/collections/importing-and-exporting-data/#importing-api-specifications). After importing, make sure to edit the imported collection and change the `baseUrl` variable to the value of the serverless `LoopbackPostman` `Stack Outputs`. This will make sure Postman works with your serverless API Gateway rather than the loopback4 only path. 62 | 63 | ## Cleaning Up The Sample 64 | 65 | Once you are done with the sample environment, avoid unnecessary AWS charges by removing your serverless deployment with the command `npm run sls-cleanup`. 66 | 67 | ## Known Issues 68 | 69 | While the serverless deployment works fine, invoking the API through API explorer never reaches API Gateway as CloudFront responds with Code 403: 70 | 71 | **Response body** 72 | ``` 73 | { 74 | "message": "Forbidden" 75 | } 76 | ``` 77 | **Response headers** 78 | ``` 79 | content-length: 24 80 | content-type: application/json 81 | date: Sun14 Jun 2020 14:14:09 GMT via: 1.1 0dfe6f02dbba7c39906cae47653ae6b3.cloudfront.net (CloudFront) 82 | x-amz-apigw-id: OOYDwFjZiYcFSAA= 83 | x-amz-cf-id: 3SvwvE27qb1KIrS34rgRRwet-QOkfZyFeJOIMUbHMgi6dA5-BgBEeg== 84 | x-amz-cf-pop: DEN50-C2 85 | x-amzn-errortype: ForbiddenException 86 | x-amzn-requestid: 31a0c024-6ffe-4c3c-b9d9-adfca9add4b0 87 | x-cache: Error from cloudfront 88 | x-firefox-spdy: h2 89 | ``` 90 | ## License 91 | 92 | MIT © [BotBits<sup>SM</sup>](https://github.com/botbits) 93 | -------------------------------------------------------------------------------- /examples/lb4-express/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [2.2.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.2.2...@loopback/example-express-composition@2.2.3) (2020-06-11) 7 | 8 | **Note:** Version bump only for package @loopback/example-express-composition 9 | 10 | 11 | 12 | 13 | 14 | ## [2.2.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.2.1...@loopback/example-express-composition@2.2.2) (2020-05-28) 15 | 16 | **Note:** Version bump only for package @loopback/example-express-composition 17 | 18 | 19 | 20 | 21 | 22 | ## [2.2.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.2.0...@loopback/example-express-composition@2.2.1) (2020-05-20) 23 | 24 | **Note:** Version bump only for package @loopback/example-express-composition 25 | 26 | 27 | 28 | 29 | 30 | # [2.2.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.1.1...@loopback/example-express-composition@2.2.0) (2020-05-19) 31 | 32 | 33 | ### Features 34 | 35 | * upgrade to TypeScript 3.9.x ([3300e45](https://github.com/strongloop/loopback-next/commit/3300e4569ab8410bb1285f7a54d326e9d976476d)) 36 | 37 | 38 | 39 | 40 | 41 | ## [2.1.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.1.0...@loopback/example-express-composition@2.1.1) (2020-05-07) 42 | 43 | **Note:** Version bump only for package @loopback/example-express-composition 44 | 45 | 46 | 47 | 48 | 49 | # [2.1.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.6...@loopback/example-express-composition@2.1.0) (2020-04-29) 50 | 51 | 52 | ### Features 53 | 54 | * move datasource config from JSON to TS files ([6105456](https://github.com/strongloop/loopback-next/commit/6105456deb6d7acadc3e46867558311dce2d005c)) 55 | 56 | 57 | 58 | 59 | 60 | ## [2.0.6](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.5...@loopback/example-express-composition@2.0.6) (2020-04-23) 61 | 62 | **Note:** Version bump only for package @loopback/example-express-composition 63 | 64 | 65 | 66 | 67 | 68 | ## [2.0.5](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.4...@loopback/example-express-composition@2.0.5) (2020-04-22) 69 | 70 | **Note:** Version bump only for package @loopback/example-express-composition 71 | 72 | 73 | 74 | 75 | 76 | ## [2.0.4](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.3...@loopback/example-express-composition@2.0.4) (2020-04-11) 77 | 78 | **Note:** Version bump only for package @loopback/example-express-composition 79 | 80 | 81 | 82 | 83 | 84 | ## [2.0.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.2...@loopback/example-express-composition@2.0.3) (2020-04-08) 85 | 86 | 87 | ### Bug Fixes 88 | 89 | * **example-express-composition:** use an assinged port number for testing ([9a0997f](https://github.com/strongloop/loopback-next/commit/9a0997f61d1afb2376cbca29fa46ad855b5a0801)) 90 | 91 | 92 | 93 | 94 | 95 | ## [2.0.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.1...@loopback/example-express-composition@2.0.2) (2020-03-24) 96 | 97 | **Note:** Version bump only for package @loopback/example-express-composition 98 | 99 | 100 | 101 | 102 | 103 | ## [2.0.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@2.0.0...@loopback/example-express-composition@2.0.1) (2020-03-17) 104 | 105 | **Note:** Version bump only for package @loopback/example-express-composition 106 | 107 | 108 | 109 | 110 | 111 | # [2.0.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.10.4...@loopback/example-express-composition@2.0.0) (2020-03-05) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * remove ref for v4.loopback.io ([78f20c0](https://github.com/strongloop/loopback-next/commit/78f20c0ed4db5f3ce0d7b676449ba3b22526319e)) 117 | 118 | 119 | ### chore 120 | 121 | * remove support for Node.js v8.x ([4281d9d](https://github.com/strongloop/loopback-next/commit/4281d9df50f0715d32879e1442a90b643ec8f542)) 122 | 123 | 124 | ### Features 125 | 126 | * add `tslib` as dependency ([a6e0b4c](https://github.com/strongloop/loopback-next/commit/a6e0b4ce7b862764167cefedee14c1115b25e0a4)), closes [#4676](https://github.com/strongloop/loopback-next/issues/4676) 127 | * use [@param](https://github.com/param).filter and [@param](https://github.com/param).where decorators ([896ef74](https://github.com/strongloop/loopback-next/commit/896ef7485376b3aedcca01a40f828bf1ed9470ae)) 128 | 129 | 130 | ### BREAKING CHANGES 131 | 132 | * Node.js v8.x is now end of life. Please upgrade to version 133 | 10 and above. See https://nodejs.org/en/about/releases. 134 | 135 | 136 | 137 | 138 | 139 | ## [1.10.4](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.10.3...@loopback/example-express-composition@1.10.4) (2020-02-06) 140 | 141 | **Note:** Version bump only for package @loopback/example-express-composition 142 | 143 | 144 | 145 | 146 | 147 | ## [1.10.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.10.2...@loopback/example-express-composition@1.10.3) (2020-02-05) 148 | 149 | 150 | ### Bug Fixes 151 | 152 | * update clean script for examples to be compatible with `lb4 example` ([d9f5741](https://github.com/strongloop/loopback-next/commit/d9f574160f6edbf73a8f728cd3695ca69297148a)) 153 | 154 | 155 | 156 | 157 | 158 | ## [1.10.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.10.1...@loopback/example-express-composition@1.10.2) (2020-01-27) 159 | 160 | **Note:** Version bump only for package @loopback/example-express-composition 161 | 162 | 163 | 164 | 165 | 166 | ## [1.10.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.10.0...@loopback/example-express-composition@1.10.1) (2020-01-07) 167 | 168 | **Note:** Version bump only for package @loopback/example-express-composition 169 | 170 | 171 | 172 | 173 | 174 | # [1.10.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.9.2...@loopback/example-express-composition@1.10.0) (2020-01-07) 175 | 176 | 177 | ### Features 178 | 179 | * add title property to ping response schema definition ([b8b7490](https://github.com/strongloop/loopback-next/commit/b8b7490ce29d0973208ba38c3365de9091b7a795)) 180 | 181 | 182 | 183 | 184 | 185 | ## [1.9.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.9.1...@loopback/example-express-composition@1.9.2) (2019-12-09) 186 | 187 | **Note:** Version bump only for package @loopback/example-express-composition 188 | 189 | 190 | 191 | 192 | 193 | ## [1.9.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.9.0...@loopback/example-express-composition@1.9.1) (2019-11-25) 194 | 195 | **Note:** Version bump only for package @loopback/example-express-composition 196 | 197 | 198 | 199 | 200 | 201 | # [1.9.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.8.1...@loopback/example-express-composition@1.9.0) (2019-11-12) 202 | 203 | 204 | ### Features 205 | 206 | * **cli:** generate datasource json with '.config.json` extension ([51d8f7b](https://github.com/strongloop/loopback-next/commit/51d8f7b20ec59f888fd6d0634efb47d111f00ef7)) 207 | 208 | 209 | 210 | 211 | 212 | ## [1.8.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.8.0...@loopback/example-express-composition@1.8.1) (2019-10-24) 213 | 214 | **Note:** Version bump only for package @loopback/example-express-composition 215 | 216 | 217 | 218 | 219 | 220 | # [1.8.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.7.1...@loopback/example-express-composition@1.8.0) (2019-10-07) 221 | 222 | 223 | ### Features 224 | 225 | * **example-express-composition:** add code path to allow life cycle observers ([8d3401d](https://github.com/strongloop/loopback-next/commit/8d3401d)) 226 | 227 | 228 | 229 | 230 | 231 | ## [1.7.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.7.0...@loopback/example-express-composition@1.7.1) (2019-09-28) 232 | 233 | **Note:** Version bump only for package @loopback/example-express-composition 234 | 235 | 236 | 237 | 238 | 239 | # [1.7.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.6.0...@loopback/example-express-composition@1.7.0) (2019-09-27) 240 | 241 | 242 | ### Features 243 | 244 | * self host oas spec by default on relative path in explorer ([887556e](https://github.com/strongloop/loopback-next/commit/887556e)) 245 | 246 | 247 | 248 | 249 | 250 | # [1.6.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.6...@loopback/example-express-composition@1.6.0) (2019-09-17) 251 | 252 | 253 | ### Features 254 | 255 | * use descriptive title to describe schema of POST (create) request bodies ([8f49a45](https://github.com/strongloop/loopback-next/commit/8f49a45)) 256 | 257 | 258 | 259 | 260 | 261 | ## [1.5.6](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.5...@loopback/example-express-composition@1.5.6) (2019-09-06) 262 | 263 | **Note:** Version bump only for package @loopback/example-express-composition 264 | 265 | 266 | 267 | 268 | 269 | ## [1.5.5](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.4...@loopback/example-express-composition@1.5.5) (2019-09-03) 270 | 271 | **Note:** Version bump only for package @loopback/example-express-composition 272 | 273 | 274 | 275 | 276 | 277 | ## [1.5.4](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.3...@loopback/example-express-composition@1.5.4) (2019-08-19) 278 | 279 | **Note:** Version bump only for package @loopback/example-express-composition 280 | 281 | 282 | 283 | 284 | 285 | ## [1.5.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.2...@loopback/example-express-composition@1.5.3) (2019-08-15) 286 | 287 | **Note:** Version bump only for package @loopback/example-express-composition 288 | 289 | 290 | 291 | 292 | 293 | ## [1.5.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.1...@loopback/example-express-composition@1.5.2) (2019-08-15) 294 | 295 | **Note:** Version bump only for package @loopback/example-express-composition 296 | 297 | 298 | 299 | 300 | 301 | ## [1.5.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.5.0...@loopback/example-express-composition@1.5.1) (2019-07-31) 302 | 303 | **Note:** Version bump only for package @loopback/example-express-composition 304 | 305 | 306 | 307 | 308 | 309 | # [1.5.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.6...@loopback/example-express-composition@1.5.0) (2019-07-26) 310 | 311 | 312 | ### Features 313 | 314 | * update examples and docs to use getModelSchemaRef ([99758b1](https://github.com/strongloop/loopback-next/commit/99758b1)) 315 | 316 | 317 | 318 | 319 | 320 | ## [1.4.6](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.5...@loopback/example-express-composition@1.4.6) (2019-07-17) 321 | 322 | 323 | ### Bug Fixes 324 | 325 | * **example-express-composition:** exclude id from POST request body ([9158142](https://github.com/strongloop/loopback-next/commit/9158142)) 326 | 327 | 328 | 329 | 330 | 331 | ## [1.4.5](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.4...@loopback/example-express-composition@1.4.5) (2019-06-28) 332 | 333 | 334 | ### Bug Fixes 335 | 336 | * **example-express-composition:** allow partial updates via PATCH ([15189ee](https://github.com/strongloop/loopback-next/commit/15189ee)) 337 | 338 | 339 | 340 | 341 | 342 | ## [1.4.4](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.3...@loopback/example-express-composition@1.4.4) (2019-06-21) 343 | 344 | **Note:** Version bump only for package @loopback/example-express-composition 345 | 346 | 347 | 348 | 349 | 350 | ## [1.4.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.2...@loopback/example-express-composition@1.4.3) (2019-06-20) 351 | 352 | **Note:** Version bump only for package @loopback/example-express-composition 353 | 354 | 355 | 356 | 357 | 358 | ## [1.4.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.1...@loopback/example-express-composition@1.4.2) (2019-06-17) 359 | 360 | 361 | ### Bug Fixes 362 | 363 | * remove forgotten references to tslint ([faa0a92](https://github.com/strongloop/loopback-next/commit/faa0a92)) 364 | 365 | 366 | 367 | 368 | 369 | ## [1.4.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.4.0...@loopback/example-express-composition@1.4.1) (2019-06-06) 370 | 371 | **Note:** Version bump only for package @loopback/example-express-composition 372 | 373 | 374 | 375 | 376 | 377 | # [1.4.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.3.3...@loopback/example-express-composition@1.4.0) (2019-06-03) 378 | 379 | 380 | ### Bug Fixes 381 | 382 | * **example-express-composition:** update migrate script file location ([8a6c072](https://github.com/strongloop/loopback-next/commit/8a6c072)) 383 | 384 | 385 | ### Features 386 | 387 | * add navigational properties to find* methods ([1f0aa0b](https://github.com/strongloop/loopback-next/commit/1f0aa0b)) 388 | * replace tslint with eslint ([44185a7](https://github.com/strongloop/loopback-next/commit/44185a7)) 389 | 390 | 391 | 392 | 393 | 394 | ## [1.3.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.3.2...@loopback/example-express-composition@1.3.3) (2019-05-31) 395 | 396 | **Note:** Version bump only for package @loopback/example-express-composition 397 | 398 | 399 | 400 | 401 | 402 | ## [1.3.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.3.1...@loopback/example-express-composition@1.3.2) (2019-05-30) 403 | 404 | **Note:** Version bump only for package @loopback/example-express-composition 405 | 406 | 407 | 408 | 409 | 410 | ## [1.3.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.3.0...@loopback/example-express-composition@1.3.1) (2019-05-23) 411 | 412 | **Note:** Version bump only for package @loopback/example-express-composition 413 | 414 | 415 | 416 | 417 | 418 | # [1.3.0](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.11...@loopback/example-express-composition@1.3.0) (2019-05-14) 419 | 420 | 421 | ### Features 422 | 423 | * add lb3 application ([bf60011](https://github.com/strongloop/loopback-next/commit/bf60011)) 424 | 425 | 426 | 427 | 428 | 429 | ## [1.2.11](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.10...@loopback/example-express-composition@1.2.11) (2019-05-10) 430 | 431 | **Note:** Version bump only for package @loopback/example-express-composition 432 | 433 | 434 | 435 | 436 | 437 | ## [1.2.10](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.9...@loopback/example-express-composition@1.2.10) (2019-05-09) 438 | 439 | **Note:** Version bump only for package @loopback/example-express-composition 440 | 441 | 442 | 443 | 444 | 445 | ## [1.2.9](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.8...@loopback/example-express-composition@1.2.9) (2019-05-06) 446 | 447 | **Note:** Version bump only for package @loopback/example-express-composition 448 | 449 | 450 | 451 | 452 | 453 | ## [1.2.8](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.7...@loopback/example-express-composition@1.2.8) (2019-04-26) 454 | 455 | **Note:** Version bump only for package @loopback/example-express-composition 456 | 457 | 458 | 459 | 460 | 461 | ## [1.2.7](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.6...@loopback/example-express-composition@1.2.7) (2019-04-20) 462 | 463 | **Note:** Version bump only for package @loopback/example-express-composition 464 | 465 | 466 | 467 | 468 | 469 | ## [1.2.6](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.5...@loopback/example-express-composition@1.2.6) (2019-04-11) 470 | 471 | **Note:** Version bump only for package @loopback/example-express-composition 472 | 473 | 474 | 475 | 476 | 477 | ## [1.2.5](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.4...@loopback/example-express-composition@1.2.5) (2019-04-09) 478 | 479 | **Note:** Version bump only for package @loopback/example-express-composition 480 | 481 | 482 | 483 | 484 | 485 | ## [1.2.4](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.3...@loopback/example-express-composition@1.2.4) (2019-04-05) 486 | 487 | **Note:** Version bump only for package @loopback/example-express-composition 488 | 489 | 490 | 491 | 492 | 493 | ## [1.2.3](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.2...@loopback/example-express-composition@1.2.3) (2019-03-22) 494 | 495 | **Note:** Version bump only for package @loopback/example-express-composition 496 | 497 | 498 | 499 | 500 | 501 | ## [1.2.2](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.1...@loopback/example-express-composition@1.2.2) (2019-03-22) 502 | 503 | **Note:** Version bump only for package @loopback/example-express-composition 504 | 505 | 506 | 507 | 508 | 509 | ## [1.2.1](https://github.com/strongloop/loopback-next/compare/@loopback/example-express-composition@1.2.0...@loopback/example-express-composition@1.2.1) (2019-03-12) 510 | 511 | 512 | ### Bug Fixes 513 | 514 | * **example-express-composition:** update npm files ([d0fb755](https://github.com/strongloop/loopback-next/commit/d0fb755)) 515 | * **example-express-composition:** use rest options ([7e22757](https://github.com/strongloop/loopback-next/commit/7e22757)) 516 | 517 | 518 | 519 | 520 | 521 | # 1.2.0 (2019-03-01) 522 | 523 | 524 | ### Bug Fixes 525 | 526 | * **example-express-composition:** fix name of example ([3a9daf6](https://github.com/strongloop/loopback-next/commit/3a9daf6)) 527 | * **example-express-composition:** remove prepublishOnly and lbApp.start() ([c095a6c](https://github.com/strongloop/loopback-next/commit/c095a6c)) 528 | 529 | 530 | ### Features 531 | 532 | * add express example ([dd2400e](https://github.com/strongloop/loopback-next/commit/dd2400e)) 533 | 534 | 535 | 536 | 537 | 538 | # 1.1.0 (2019-03-01) 539 | 540 | 541 | ### Features 542 | 543 | * add express example ([dd2400e](https://github.com/strongloop/loopback-next/commit/dd2400e)) 544 | 545 | 546 | 547 | 548 | 549 | # Change Log 550 | --------------------------------------------------------------------------------