├── .jshintignore ├── bin ├── datetime-request └── remote-method-request ├── client └── README.md ├── server ├── component-config.json ├── boot │ ├── authentication.js │ ├── root.js │ └── print-models.js ├── middleware │ ├── datetime.js │ └── tracker.js ├── config.json ├── datasources.json ├── model-config.json ├── middleware.json ├── bin │ └── send-email.js └── server.js ├── .npmignore ├── CODEOWNERS ├── examples └── async-boot-script │ ├── README.md │ ├── create-car.js │ └── server.js ├── .gitignore ├── common └── models │ ├── car.json │ └── car.js ├── .editorconfig ├── .jshintrc ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── stale.yml ├── package.json ├── LICENSE ├── test └── rest_api_test.js ├── CONTRIBUTING.md └── README.md /.jshintignore: -------------------------------------------------------------------------------- 1 | /client/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /bin/datetime-request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | curl localhost:3000/datetime 3 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | ## Client 2 | 3 | This is the place for your application front-end files. 4 | -------------------------------------------------------------------------------- /server/component-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "loopback-component-explorer": { 3 | "mountPath": "/explorer" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /bin/remote-method-request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | curl -XPOST localhost:3000/api/cars/rev-engine -H 'content-type:application/json' -d '{"sound":"vroom"}' 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .project 3 | *.sublime-* 4 | .DS_Store 5 | *.seed 6 | *.log 7 | *.csv 8 | *.dat 9 | *.out 10 | *.pid 11 | *.swp 12 | *.swo 13 | node_modules 14 | coverage 15 | *.tgz 16 | *.xml 17 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners, 3 | # the last matching pattern has the most precedence. 4 | 5 | # Core team members from IBM 6 | * @superkhau 7 | -------------------------------------------------------------------------------- /examples/async-boot-script/README.md: -------------------------------------------------------------------------------- 1 | 1. Replace [`server.js`](../../server/server.js) with [the one in this directory](server.js) 2 | 2. Copy [`create-car.js`](create-car.js) script to the [boot scripts directory](../../server/boot) 3 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /common/models/car.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "car", 3 | "base": "PersistedModel", 4 | "idInjection": true, 5 | "properties": { 6 | "make": { 7 | "type": "string" 8 | }, 9 | "model": { 10 | "type": "string" 11 | } 12 | }, 13 | "validations": [], 14 | "relations": {}, 15 | "acls": [], 16 | "methods": [] 17 | } 18 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /server/boot/authentication.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function enableAuthentication(server) { 7 | // enable authentication 8 | server.enableAuth(); 9 | }; 10 | -------------------------------------------------------------------------------- /server/middleware/datetime.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function() { 7 | return function datetime(req, res, next) { 8 | console.log('Date time middleware triggered.'); 9 | 10 | res.json({datetime: new Date()}); 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /server/boot/root.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function(server) { 7 | // Install a `/` route that returns server status 8 | var router = server.loopback.Router(); 9 | router.get('/', server.loopback.status()); 10 | server.use(router); 11 | }; 12 | -------------------------------------------------------------------------------- /server/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "restApiRoot": "/api", 3 | "host": "0.0.0.0", 4 | "port": 3000, 5 | "remoting": { 6 | "context": false, 7 | "rest": { 8 | "normalizeHttpPath": false, 9 | "xml": false 10 | }, 11 | "json": { 12 | "strict": false, 13 | "limit": "100kb" 14 | }, 15 | "urlencoded": { 16 | "extended": true, 17 | "limit": "100kb" 18 | }, 19 | "cors": false, 20 | "handleErrors": false 21 | }, 22 | "legacyExplorer": false 23 | } 24 | -------------------------------------------------------------------------------- /server/datasources.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": { 3 | "name": "db", 4 | "connector": "memory" 5 | }, 6 | "emailDs": { 7 | "name": "emailDs", 8 | "connector": "mail", 9 | "transports": [{ 10 | "type": "smtp", 11 | "host": "smtp.gmail.com", 12 | "secure": true, 13 | "port": 465, 14 | "tls": { 15 | "rejectUnauthorized": false 16 | }, 17 | "auth": { 18 | "user": "email@address.com", 19 | "pass": "password" 20 | } 21 | }] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/boot/print-models.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function(app) { 7 | var models = []; 8 | 9 | Object.keys(app.models).forEach(function(model) { 10 | var modelName = app.models[model].modelName; 11 | 12 | if (models.indexOf(modelName) === -1) 13 | models.push(modelName); 14 | }); 15 | 16 | console.log('Models: ', models); 17 | }; 18 | -------------------------------------------------------------------------------- /examples/async-boot-script/create-car.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function(app, cb) { 7 | app.models.Car.create({ 8 | make: 'honda', 9 | model: 'civic' 10 | }, function(err, car) { 11 | if (err) 12 | return cb(err); 13 | 14 | console.log('A `car` instance has been created from a boot script:', car); 15 | 16 | cb(); 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "eqeqeq": true, 7 | "eqnull": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": "nofunc", 11 | "newcap": true, 12 | "nonew": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": false, 18 | "trailing": true, 19 | "sub": true, 20 | "maxlen": 80, 21 | "globals" : { 22 | /* MOCHA */ 23 | "describe" : false, 24 | "it" : false, 25 | "before" : false, 26 | "after" : false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/middleware/tracker.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function() { 7 | return function tracker(req, res, next) { 8 | console.log('Request tracking middleware triggered on %s.', req.url); 9 | 10 | var start = process.hrtime(); 11 | 12 | res.once('finish', function() { 13 | var diff = process.hrtime(start); 14 | var ms = diff[0] * 1e3 + diff[1] * 1e-6; 15 | 16 | console.log('The request processing time is %d ms.', ms); 17 | }); 18 | 19 | next(); 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /server/model-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "sources": [ 4 | "loopback/common/models", 5 | "loopback/server/models", 6 | "../common/models", 7 | "./models" 8 | ] 9 | }, 10 | "User": { 11 | "dataSource": "db" 12 | }, 13 | "AccessToken": { 14 | "dataSource": "db", 15 | "public": false 16 | }, 17 | "ACL": { 18 | "dataSource": "db", 19 | "public": false 20 | }, 21 | "RoleMapping": { 22 | "dataSource": "db", 23 | "public": false 24 | }, 25 | "Role": { 26 | "dataSource": "db", 27 | "public": false 28 | }, 29 | "car": { 30 | "dataSource": "db", 31 | "public": true 32 | }, 33 | "Email": { 34 | "dataSource": "emailDs" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/middleware.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial:before": { 3 | "loopback#favicon": {} 4 | }, 5 | "initial": { 6 | "compression": {}, 7 | "./middleware/tracker": {}, 8 | "cors": { 9 | "params": { 10 | "origin": true, 11 | "credentials": true, 12 | "maxAge": 86400 13 | } 14 | } 15 | }, 16 | "session": { 17 | }, 18 | "auth": { 19 | }, 20 | "parse": { 21 | }, 22 | "routes": { 23 | "loopback#rest": { 24 | "paths": ["${restApiRoot}"] 25 | }, 26 | "./middleware/datetime": { 27 | "paths": "/datetime" 28 | } 29 | }, 30 | "files": { 31 | }, 32 | "final": { 33 | "loopback#urlNotFound": {} 34 | }, 35 | "final:after": { 36 | "strong-error-handler": {} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 4 | #### Related issues 5 | 6 | 12 | 13 | - connect to 14 | 15 | ### Checklist 16 | 17 | 22 | 23 | - [ ] New tests added or existing tests modified to cover all changes 24 | - [ ] Code conforms with the [style 25 | guide](http://loopback.io/doc/en/contrib/style-guide.html) 26 | -------------------------------------------------------------------------------- /server/bin/send-email.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | var dsConfig = require('../datasources.json'); 7 | var app = require('../server.js'); 8 | 9 | if (process.env.CI) { 10 | console.log('skipping sending email from CI'); 11 | } 12 | var yourEmailAddress = dsConfig.emailDs.transports[0].auth.user; 13 | 14 | app.models.Email.send({ 15 | to: null, // your email address 16 | from: null, // your email address 17 | subject: 'The email subject', 18 | text: 'HTML tags are not converted' 19 | //html: 'HTML tags are converted' 20 | }, function(err) { 21 | if (err) throw err; 22 | console.log('> email sent successfully'); 23 | }); 24 | -------------------------------------------------------------------------------- /examples/async-boot-script/server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | var loopback = require('loopback'); 7 | var boot = require('loopback-boot'); 8 | 9 | var app = module.exports = loopback(); 10 | 11 | app.start = function() { 12 | // start the web server 13 | return app.listen(function() { 14 | app.emit('started'); 15 | console.log('Web server listening at: %s', app.get('url')); 16 | }); 17 | }; 18 | 19 | // Bootstrap the application, configure models, datasources and middleware. 20 | // Sub-apps like REST API are mounted via boot scripts. 21 | boot(app, __dirname, function(err) { 22 | if (err) throw err; 23 | 24 | // start the server if `$ node server.js` 25 | if (require.main === module) { 26 | app.start(); 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | # Description/Steps to reproduce 11 | 12 | 16 | 17 | # Link to reproduction sandbox 18 | 19 | 24 | 25 | # Expected result 26 | 27 | 30 | 31 | # Additional information 32 | 33 | 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loopback-example-app-logic", 3 | "version": "1.0.0", 4 | "description": "loopback-example-app-logic", 5 | "engines": { 6 | "node": ">=6" 7 | }, 8 | "main": "server/server.js", 9 | "scripts": { 10 | "pretest": "jshint .", 11 | "start": "node .", 12 | "test": "mocha -R spec test" 13 | }, 14 | "dependencies": { 15 | "compression": "^1.0.3", 16 | "cors": "^2.8.1", 17 | "loopback": "^3.0.0", 18 | "loopback-boot": "^2.6.5", 19 | "serve-favicon": "^2.0.1", 20 | "strong-error-handler": "^3.2.0" 21 | }, 22 | "optionalDependencies": { 23 | "loopback-component-explorer": "^6.3.1" 24 | }, 25 | "devDependencies": { 26 | "jshint": "^2.5.6", 27 | "mocha": "^2.3.3", 28 | "supertest": "^1.1.0" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/strongloop/loopback-example-app-logic" 33 | }, 34 | "license": "MIT", 35 | "author": "IBM Corp." 36 | } 37 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - critical 10 | - p1 11 | - major 12 | # Label to use when marking an issue as stale 13 | staleLabel: stale 14 | # Comment to post when marking an issue as stale. Set to `false` to disable 15 | markComment: > 16 | This issue has been automatically marked as stale because it has not had 17 | recent activity. It will be closed if no further activity occurs. Thank you 18 | for your contributions. 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: > 21 | This issue has been closed due to continued inactivity. Thank you for your understanding. 22 | If you believe this to be in error, please contact one of the code owners, 23 | listed in the `CODEOWNERS` file at the top-level of this repository. 24 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | var loopback = require('loopback'); 7 | var boot = require('loopback-boot'); 8 | 9 | var app = module.exports = loopback(); 10 | 11 | app.start = function() { 12 | // start the web server 13 | return app.listen(function() { 14 | app.emit('started'); 15 | var baseUrl = app.get('url').replace(/\/$/, ''); 16 | console.log('Web server listening at: %s', baseUrl); 17 | if (app.get('loopback-component-explorer')) { 18 | var explorerPath = app.get('loopback-component-explorer').mountPath; 19 | console.log('Browse your REST API at %s%s', baseUrl, explorerPath); 20 | } 21 | }); 22 | }; 23 | 24 | // Bootstrap the application, configure models, datasources and middleware. 25 | // Sub-apps like REST API are mounted via boot scripts. 26 | boot(app, __dirname, function(err) { 27 | if (err) throw err; 28 | 29 | // start the server if `$ node server.js` 30 | if (require.main === module) 31 | app.start(); 32 | }); 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) IBM Corp. 2015,2017. All Rights Reserved. 2 | Node module: loopback-example-app-logic 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 | -------------------------------------------------------------------------------- /test/rest_api_test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | /* jshint camelcase: false */ 7 | var app = require('../server/server'); 8 | var request = require('supertest'); 9 | var assert = require('assert'); 10 | var loopback = require('loopback'); 11 | 12 | function json(verb, url) { 13 | return request(app)[verb](url) 14 | .set('Content-Type', 'application/json') 15 | .set('Accept', 'application/json') 16 | .expect('Content-Type', /json/); 17 | } 18 | 19 | describe('REST API request', function() { 20 | it('should take a sound and repeat it 3 times', function(done){ 21 | json('post', '/api/cars/rev-engine') 22 | .send({ 23 | sound: 'vroom' 24 | }) 25 | .expect(200) 26 | .end(function(err, res){ 27 | assert(typeof res.body === 'object'); 28 | assert(res.body.engineSound); 29 | assert.equal(res.body.engineSound, 'vroom vroom vroom'); 30 | done(); 31 | }); 32 | }); 33 | 34 | it('should not crash the server when posting a bad id', function(done){ 35 | json('post', '/api/cars/foobar') 36 | .send({}) 37 | .expect(404, done); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /common/models/car.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: loopback-example-app-logic 3 | // This file is licensed under the MIT License. 4 | // License text available at https://opensource.org/licenses/MIT 5 | 6 | module.exports = function(Car) { 7 | // remote method 8 | Car.revEngine = function(sound, cb) { 9 | cb(null, sound + ' ' + sound + ' ' + sound); 10 | }; 11 | Car.remoteMethod( 12 | 'revEngine', 13 | { 14 | accepts: [{arg: 'sound', type: 'string'}], 15 | returns: {arg: 'engineSound', type: 'string'}, 16 | http: {path:'/rev-engine', verb: 'post'} 17 | } 18 | ); 19 | 20 | // remote method before hooks 21 | Car.beforeRemote('revEngine', function(context, unused, next) { 22 | console.log('Putting in the car key, starting the engine.'); 23 | next(); 24 | }); 25 | 26 | // afterInitialize is a model hook which is still used in loopback 27 | Car.afterInitialize = function() { 28 | // http://docs.strongloop.com/display/public/LB/Model+hooks#Modelhooks-afterInitialize 29 | console.log('> afterInitialize triggered'); 30 | }; 31 | 32 | // the rest are all operation hooks 33 | // - http://docs.strongloop.com/display/public/LB/Operation+hooks 34 | Car.observe('before save', function(ctx, next) { 35 | console.log('> before save triggered:', ctx.Model.modelName, ctx.instance || 36 | ctx.data); 37 | next(); 38 | }); 39 | Car.observe('after save', function(ctx, next) { 40 | console.log('> after save triggered:', ctx.Model.modelName, ctx.instance); 41 | next(); 42 | }); 43 | Car.observe('before delete', function(ctx, next) { 44 | console.log('> before delete triggered:', 45 | ctx.Model.modelName, ctx.instance); 46 | next(); 47 | }); 48 | Car.observe('after delete', function(ctx, next) { 49 | console.log('> after delete triggered:', 50 | ctx.Model.modelName, (ctx.instance || ctx.where)); 51 | next(); 52 | }); 53 | 54 | // remote method after hook 55 | Car.afterRemote('revEngine', function(context, remoteMethodOutput, next) { 56 | console.log('Turning off the engine, removing the key.'); 57 | next(); 58 | }); 59 | 60 | // model operation hook 61 | Car.observe('before save', function(ctx, next) { 62 | if (ctx.instance) { 63 | console.log('About to save a car instance:', ctx.instance); 64 | } else { 65 | console.log('About to update cars that match the query %j:', ctx.where); 66 | } 67 | next(); 68 | }); 69 | }; 70 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing ### 2 | 3 | Thank you for your interest in `loopback-example-app-logic`, an open source project 4 | administered by StrongLoop. 5 | 6 | Contributing to `loopback-example-app-logic` is easy. In a few simple steps: 7 | 8 | * Ensure that your effort is aligned with the project's roadmap by 9 | talking to the maintainers, especially if you are going to spend a 10 | lot of time on it. 11 | 12 | * Make something better or fix a bug. 13 | 14 | * Adhere to code style outlined in the [Google C++ Style Guide][] and 15 | [Google Javascript Style Guide][]. 16 | 17 | * Sign the [Contributor License Agreement](https://cla.strongloop.com/strongloop/loopback-example-app-logic) 18 | 19 | * Submit a pull request through Github. 20 | 21 | 22 | ### Contributor License Agreement ### 23 | 24 | ``` 25 | Individual Contributor License Agreement 26 | 27 | By signing this Individual Contributor License Agreement 28 | ("Agreement"), and making a Contribution (as defined below) to 29 | StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and 30 | agree to the following terms and conditions for Your present and 31 | future Contributions submitted to StrongLoop. Except for the license 32 | granted in this Agreement to StrongLoop and recipients of software 33 | distributed by StrongLoop, You reserve all right, title, and interest 34 | in and to Your Contributions. 35 | 36 | 1. Definitions 37 | 38 | "You" or "Your" shall mean the copyright owner or the individual 39 | authorized by the copyright owner that is entering into this 40 | Agreement with StrongLoop. 41 | 42 | "Contribution" shall mean any original work of authorship, 43 | including any modifications or additions to an existing work, that 44 | is intentionally submitted by You to StrongLoop for inclusion in, 45 | or documentation of, any of the products owned or managed by 46 | StrongLoop ("Work"). For purposes of this definition, "submitted" 47 | means any form of electronic, verbal, or written communication 48 | sent to StrongLoop or its representatives, including but not 49 | limited to communication or electronic mailing lists, source code 50 | control systems, and issue tracking systems that are managed by, 51 | or on behalf of, StrongLoop for the purpose of discussing and 52 | improving the Work, but excluding communication that is 53 | conspicuously marked or otherwise designated in writing by You as 54 | "Not a Contribution." 55 | 56 | 2. You Grant a Copyright License to StrongLoop 57 | 58 | Subject to the terms and conditions of this Agreement, You hereby 59 | grant to StrongLoop and recipients of software distributed by 60 | StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, 61 | royalty-free, irrevocable copyright license to reproduce, prepare 62 | derivative works of, publicly display, publicly perform, 63 | sublicense, and distribute Your Contributions and such derivative 64 | works under any license and without any restrictions. 65 | 66 | 3. You Grant a Patent License to StrongLoop 67 | 68 | Subject to the terms and conditions of this Agreement, You hereby 69 | grant to StrongLoop and to recipients of software distributed by 70 | StrongLoop a perpetual, worldwide, non-exclusive, no-charge, 71 | royalty-free, irrevocable (except as stated in this Section) 72 | patent license to make, have made, use, offer to sell, sell, 73 | import, and otherwise transfer the Work under any license and 74 | without any restrictions. The patent license You grant to 75 | StrongLoop under this Section applies only to those patent claims 76 | licensable by You that are necessarily infringed by Your 77 | Contributions(s) alone or by combination of Your Contributions(s) 78 | with the Work to which such Contribution(s) was submitted. If any 79 | entity institutes a patent litigation against You or any other 80 | entity (including a cross-claim or counterclaim in a lawsuit) 81 | alleging that Your Contribution, or the Work to which You have 82 | contributed, constitutes direct or contributory patent 83 | infringement, any patent licenses granted to that entity under 84 | this Agreement for that Contribution or Work shall terminate as 85 | of the date such litigation is filed. 86 | 87 | 4. You Have the Right to Grant Licenses to StrongLoop 88 | 89 | You represent that You are legally entitled to grant the licenses 90 | in this Agreement. 91 | 92 | If Your employer(s) has rights to intellectual property that You 93 | create, You represent that You have received permission to make 94 | the Contributions on behalf of that employer, that Your employer 95 | has waived such rights for Your Contributions, or that Your 96 | employer has executed a separate Corporate Contributor License 97 | Agreement with StrongLoop. 98 | 99 | 5. The Contributions Are Your Original Work 100 | 101 | You represent that each of Your Contributions are Your original 102 | works of authorship (see Section 8 (Submissions on Behalf of 103 | Others) for submission on behalf of others). You represent that to 104 | Your knowledge, no other person claims, or has the right to claim, 105 | any right in any intellectual property right related to Your 106 | Contributions. 107 | 108 | You also represent that You are not legally obligated, whether by 109 | entering into an agreement or otherwise, in any way that conflicts 110 | with the terms of this Agreement. 111 | 112 | You represent that Your Contribution submissions include complete 113 | details of any third-party license or other restriction (including, 114 | but not limited to, related patents and trademarks) of which You 115 | are personally aware and which are associated with any part of 116 | Your Contributions. 117 | 118 | 6. You Don't Have an Obligation to Provide Support for Your Contributions 119 | 120 | You are not expected to provide support for Your Contributions, 121 | except to the extent You desire to provide support. You may provide 122 | support for free, for a fee, or not at all. 123 | 124 | 6. No Warranties or Conditions 125 | 126 | StrongLoop acknowledges that unless required by applicable law or 127 | agreed to in writing, You provide Your Contributions on an "AS IS" 128 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 129 | EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES 130 | OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR 131 | FITNESS FOR A PARTICULAR PURPOSE. 132 | 133 | 7. Submission on Behalf of Others 134 | 135 | If You wish to submit work that is not Your original creation, You 136 | may submit it to StrongLoop separately from any Contribution, 137 | identifying the complete details of its source and of any license 138 | or other restriction (including, but not limited to, related 139 | patents, trademarks, and license agreements) of which You are 140 | personally aware, and conspicuously marking the work as 141 | "Submitted on Behalf of a Third-Party: [named here]". 142 | 143 | 8. Agree to Notify of Change of Circumstances 144 | 145 | You agree to notify StrongLoop of any facts or circumstances of 146 | which You become aware that would make these representations 147 | inaccurate in any respect. Email us at callback@strongloop.com. 148 | ``` 149 | 150 | [Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html 151 | [Google Javascript Style Guide]: https://google.github.io/styleguide/javascriptguide.xml 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # loopback-example-app-logic 2 | 3 | **⚠️ This LoopBack 3 example project is no longer maintained. Please refer to [LoopBack 4 Examples](https://loopback.io/doc/en/lb4/Examples.html) instead. ⚠️** 4 | 5 | ``` 6 | $ git clone https://github.com/strongloop/loopback-example-app-logic.git 7 | $ cd loopback-example-app-logic 8 | $ npm install 9 | $ node . 10 | # then in a different tab, run ./bin/remote-method-request or ./bin/datetime-request 11 | ``` 12 | 13 | In this example, we demonstrate remote methods, remote hooks, model operation hooks, boot scripts, middleware, and email-connector as solutions for integrating user-defined logic into a LoopBack application. 14 | 15 | 16 | ## Prerequisites 17 | 18 | Tutorials: 19 | 20 | - [Getting started with LoopBack](http://loopback.io/doc/en/lb2/Getting-started-with-LoopBack.html) 21 | 22 | Knowledge: 23 | 24 | - [LoopBack models](http://loopback.io/doc/en/lb2/Defining-models.html) 25 | - [LoopBack adding application logic](http://loopback.io/doc/en/lb2/Adding-application-logic.html) 26 | 27 | ## Procedure 28 | 29 | ### Create the application 30 | 31 | Application information: 32 | 33 | - Name: `loopback-example-app-logic` 34 | - Directory to contain the project: `loopback-example-app-logic` 35 | 36 | ``` 37 | $ lb app loopback-example-app-logic 38 | ... # follow the prompts 39 | $ cd loopback-example-app-logic 40 | ``` 41 | 42 | ### Add a model 43 | 44 | Model information: 45 | 46 | - Name: `car` 47 | - Datasource: `db (memory)` 48 | - Base class: `PersistedModel` 49 | - Expose via REST: `Yes` 50 | - Custom plural form: *Leave blank* 51 | - Properties 52 | - `make` 53 | - String 54 | - Not required 55 | - `model` 56 | - String 57 | - Not required 58 | 59 | ``` 60 | $ lb model car 61 | ... # follow the prompts 62 | ``` 63 | 64 | ### Define a remote method 65 | 66 | Define a [remote method in `car.js`](https://github.com/strongloop/loopback-example-app-logic/blob/master/common/models/car.js#L7-L18). 67 | 68 | > The remote method takes a "sound" and repeats it three times. 69 | 70 | Test it by starting the server (using `node .`) and running `curl -XPOST localhost:3000/api/cars/rev-engine -H 'content-type:application/json' -d '{"sound":"vroom"}'`. 71 | 72 | >If you are using Windows, single quotes are treated as backticks in `cmd`. This means you will have to modify the `curl` command to use and escape double quotes instead: `curl -XPOST localhost:3000/api/cars/rev-engine -H "content-type:application/json" -d "{\"sound\":\"vroom\"}"`. 73 | 74 | You should see: 75 | 76 | ``` 77 | ... 78 | {"engineSound":"vroom vroom vroom"} 79 | ``` 80 | 81 | ### Define a remote method before hook 82 | 83 | Define a [remote method before hook in `car.js`](https://github.com/strongloop/loopback-example-app-logic/blob/master/common/models/car.js#L21-L25). 84 | 85 | > The second parameter `unused` must be provided for legacy reasons. You may simply ignore it, but you must declare it to ensure `next` is the third parameter. This is a side effect of inheriting from the [`jugglingdb`](https://github.com/1602/jugglingdb) library. 86 | 87 | > `context` contains the [Express](http://expressjs.com/) request and response objects (ie. `context.req` and `context.res`). 88 | 89 | This method is triggered right before `revEngine` is called and prints a message to the console. 90 | 91 | Restart the server. 92 | 93 | ``` 94 | $ ./bin/remote-method-request 95 | ``` 96 | 97 | You should see: 98 | 99 | ``` 100 | ... 101 | Putting in the car key, starting the engine. 102 | ``` 103 | 104 | ### Define a remote method after hook 105 | 106 | Define a [remote method after hook in `car.js`](https://github.com/strongloop/loopback-example-app-logic/blob/master/common/models/car.js#L54-L58). 107 | 108 | This method is triggered after `revEngine` finishes execution and prints a message to the console. 109 | 110 | Restart the server. 111 | 112 | ``` 113 | $ ./bin/remote-method-request 114 | ``` 115 | 116 | You should see: 117 | 118 | ``` 119 | ... 120 | Turning off the engine, removing the key. 121 | ``` 122 | 123 | ### Create a boot script 124 | 125 | Create [`print-models.js`](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/boot/print-models.js) in the [`boot` directory](/server/boot). 126 | 127 | > NOTE: The [`app` argument](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/boot/print-models.js#L6) is provided by LoopBack. You can use it to access the application context, which is required when you want to retrieve models, configs, and so on. 128 | 129 | #### Asynchronous boot scripts 130 | 131 | To use asynchronous boot scripts, you have to modify [`boot`](https://github.com/strongloop/loopback-example-app-logic/blob/master/examples/async-boot-scripts/server.js#L8) to take callback. You will also need to provide an additional [`callback` argument](https://github.com/strongloop/loopback-example-app-logic/blob/master/examples/async-boot-script/create-car.js#L6) in your boot scripts. 132 | 133 | Restart the server. 134 | 135 | In the server output, you should see: 136 | 137 | ``` 138 | ... 139 | Models: [ 'User', 'AccessToken', 'ACL', 'RoleMapping', 'Role', 'car' ] 140 | ... 141 | ``` 142 | 143 | ### Define a model operation hook 144 | 145 | Define [a model operation hook in `car.js`](https://github.com/strongloop/loopback-example-app-logic/blob/master/common/models/car.js#L60-L68). 146 | 147 | Copy the `create-car.js` script to the `server/boot` directory. 148 | 149 | ``` 150 | $ cp examples/async-boot-script/create-car.js server/boot/ 151 | ``` 152 | 153 | Restart the server. 154 | 155 | You should see: 156 | 157 | ``` 158 | ... 159 | About to save a car instance: { make: 'honda', model: 'civic' } 160 | A `car` instance has been created from a boot script: { make: 'honda', model: 'civic', id: 1 } 161 | ... 162 | ``` 163 | 164 | This model operation hook is triggered **before** saving any `car` model instance. 165 | 166 | > Many other operation hooks are available, such as `access`, `before save`, `after save`, `before delete`, and `after delete`. See the [model operation hooks documentation](http://docs.strongloop.com/display/public/LB/Operation+hooks) for more information. 167 | 168 | ### Add pre-processing middleware 169 | 170 | Create the [`middleware` directory](/server/middleware) to store middleware 171 | files. 172 | 173 | ``` 174 | $ mkdir server/middleware 175 | ``` 176 | 177 | Create the [`tracker` middleware](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/middleware/tracker.js) to respond with 178 | the request processing time. 179 | 180 | Register the `tracker` middleware in [`middleware.json`](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/middleware.json#L7). 181 | 182 | > We register `tracker` in the `initial` phase because we want it configured before other middleware. See the [official middleware phases documentation](http://docs.strongloop.com/display/LB/Defining+middleware#Definingmiddleware-Middlewarephases). 183 | 184 | Restart the server. 185 | 186 | ``` 187 | $ ./bin/remote-method-request 188 | ``` 189 | 190 | You should see: 191 | 192 | ``` 193 | ... 194 | The request processing time is 28.472051 ms. 195 | ``` 196 | 197 | > Your time will be different. 198 | 199 | ### Add post-processing middleware 200 | 201 | Create the [`datetime` middleware](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/middleware/datetime.js), which responds with the current date and time when a request is made to [`localhost:3000/datetime`](http://localhost:3000/datetime). 202 | 203 | Register the `datetime` middleware in [`middleware.json`](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/middleware.json#L26-L28). 204 | 205 | [Create a shell script](https://github.com/strongloop/loopback-example-app-logic/blob/master/bin/datetime-request) to test the middleware. 206 | 207 | Restart the server. 208 | 209 | ``` 210 | $ ./bin/datetime-request 211 | ``` 212 | 213 | You should see: 214 | 215 | ``` 216 | ... 217 | {"datetime":"2015-01-14T22:54:35.708Z"} 218 | ``` 219 | 220 | > Your date and time will be different. 221 | 222 | ### Add an email connector 223 | 224 | How do you send email? 225 | 226 | 1. Configure an [email datasource](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/datasources.json#L6-L21) 227 | 2. Map the built-in `Email` model to the [the email datasource](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/model-config.json#L33-L35) 228 | 3. Send an email using the [configured model](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/bin/send-email.js#L12-L23) 229 | 230 | Notes: 231 | 232 | - This example contains a boot script that sends an email every time you start the application. 233 | - Be sure to use **YOUR** email configurations in [`datasources.json`](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/datasources.json#L18-L19) 234 | - You will need to [configure `boot()` in `server.js` to take a callback](https://github.com/strongloop/loopback-example-app-logic/blob/master/server/server.js#L8) for the application to start up properly because we use an [asynchronous boot script](https://github.com/strongloop/loopback-example-email/blob/master/server/boot/send-email.js#L3) for this example 235 | 236 | --- 237 | 238 | [More LoopBack examples](https://loopback.io/doc/en/lb3/Tutorials-and-examples.html) 239 | --------------------------------------------------------------------------------