{"id":22109297,"url":"https://github.com/pruvonet/loopback-getting-started","last_synced_at":"2025-03-24T03:46:43.975Z","repository":{"id":53677979,"uuid":"99414273","full_name":"PruvoNet/loopback-getting-started","owner":"PruvoNet","description":"Loopback application getting started","archived":false,"fork":false,"pushed_at":"2021-03-19T16:11:35.000Z","size":1454,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-29T10:17:07.566Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PruvoNet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-05T09:52:54.000Z","updated_at":"2017-08-16T09:45:31.000Z","dependencies_parsed_at":"2022-09-10T05:40:19.741Z","dependency_job_id":null,"html_url":"https://github.com/PruvoNet/loopback-getting-started","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PruvoNet%2Floopback-getting-started","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PruvoNet%2Floopback-getting-started/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PruvoNet%2Floopback-getting-started/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PruvoNet%2Floopback-getting-started/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PruvoNet","download_url":"https://codeload.github.com/PruvoNet/loopback-getting-started/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245206534,"owners_count":20577582,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-01T09:31:14.786Z","updated_at":"2025-03-24T03:46:43.956Z","avatar_url":"https://github.com/PruvoNet.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# loopback-getting-started\n\nA simple tutorial for getting started with the Loopback framework.\n\nThis tutrial will cover:\n\n  - Setting up a working web applicaiton with frontend and backend\n  - Set up models for the web application\n  - Defining the models relations and ACLs\n  - Authentication and Authrization features\n  - 3rd party login support\n  - Connecting the models to a real DB\n  - Angular frontend that communicates with the backend server\n\n[presentation](https://github.com/PruvoNet/loopback-getting-started/blob/master/meetup-from%20idea%20to%20production.pdf)\n\nGoal\n====\n\nWe will create a simple web application (MyNotes) that will enable users to create and manage personal notes with the abaility to archive them once they are irrelevant.\n\nTable of contents\n=================\n\n  * [Set up](#set-up)\n  * [Generate the application](#generate-the-application)\n  * [Attach a DB](#attach-a-db)\n  * [Auto create DB schemas](#auto-create-db-schemas)\n  * [Create models](#create-models)\n  * [Add archive method to the Note model](#add-archive-method-to-the-note-model)\n  * [Extend user models](#extend-user-models)\n  * [Define relations](#define-relations)\n  * [Define ACLs](#define-acls)\n  * [Add the frontend](#add-the-frontend)\n  * [Interactions between the frontend and the backend](#interactions-between-the-frontend-and-the-backend)\n  * [Login using Facebook](#login-using-facebook)\n  * [Summary](#summary)\n  * [Credits](#credits)\n \nSet up\n======\n\nPlease make sure you have the following:\n  - [Node.js](https://nodejs.org/)\n  - [NPM](https://www.npmjs.com/)\n  - loopback global npm libraries:\n\n```sh\n$ npm install -g loopback-cli\n$ npm install -g strongloop\n$ npm install -g loopback-sdk-angular-cli\n```\n\nGenerate the application\n========================\n\n```sh\n$ mkdir MyNotes\n$ cd MyNotes\n$ lb\n? What's the name of your application? MyNotes\n? Which version of LoopBack would you like to use? 3.x (current)\n? What kind of application do you have in mind? api-server (A LoopBack API server with local User auth)\n$ node server/server.js\n```\n\nNow we have a working server. You can explore the main page by opening http://localhost:3000/  \nAll models in loopback comes with a full REST representation out of the box.  \nYou can explore and interact with your models at any time by opening the Swagger explorer at http://localhost:3000/explorer\n\nLoopback comes with a builtin User model for handling users.  \nTry and create a user by using the POST method on the /Users endpoint and giving the following user object:\n```json\n{\n    \"email\": \"your@email.com\",\n    \"password\": \"12345\"\n}\n```\nNotice that we got a valid response from the server, stating the user was created.\n\nLets try to get all the users we have by running the GET method on the /Users endpoint.  \nNotice we got a 401 error code - which makes sense, as users data is sensetive and as such no one can read it without the proper access - we will discuss ACLs later on.\n\nAttach a DB\n===========\n\nLoopback comes with an in-memory DB for developing purposes which makes it easier to start working right away.  \nConnecting a real DB is as easy as a cli command.\n\n\u003e You can skip this step if you don't have a test DB set up\n\nFirst lets remove the in-memory db defintion from `server/datasources.js`\n\nFor the purpose of this tutorial, we will connect a MSSQL DB:\n```sh\n$ lb datasource\n? Enter the datasource name: db\n? Select the connector for sql: Microsoft SQL (supported by StrongLoop)\n? Connection String url to override other settings (eg: mssql://username:password@localhost/database):\n? host: testmyapp.database.windows.net\n? port: 1433\n? user: myapp\n? password: **********\n? database: testMyApp\n? Install loopback-connector-mssql@^2.5 Yes\n```\n\nNotice that the `server/datasources.json` file has a new \"db\" object that describes the DB connection we just set up.\n\nSince MSSQL requires an encrypted connection, we need to declare it in the matching object by adding the following to its definition:\n```json\n\"options\": \n{\n      \"encrypt\": true\n}\n```\n\nAuto create DB schemas\n=====================\n\nLoopback can auto create all the schemas that describe our models. This is very convenient for developing since we don't want to create the schemas ourselves.\n\nTo enable this feature, we need to add the following file:\n`server/boot/autoupdate.js`\n```javascript\n'use strict';\n\nmodule.exports = function (app) {\n  var datasources = require('../datasources.json');\n\n  function autoUpdateAll() {\n    Object.keys(datasources).forEach(function (key) {\n      var DS = app.dataSources[key];\n      if (DS.connected) {\n        DS.autoupdate(function (err) {\n          if (err) throw err;\n          console.log('DS ' + key + ' updated');\n        });\n      } else {\n        DS.once('connected', function () {\n          DS.autoupdate(function (err) {\n            if (err) throw err;\n            console.log('DS ' + key + ' updated');\n          });\n        });\n      }\n    });\n  }\n\n  autoUpdateAll();\n};\n```\n\n\u003e In production you should disable this feature as it changes schemas even if they alredy exists, which can lead to data loss!\n\nRerun the application and create a user again (it was deleted from last time as it was saved in memory)  \nOpen your prefrence of DB explorer and notice that a lof of tables were auto created for you and that the user table has 1 entry of the user you just created.\n\nCreate models\n=============\n\nAs our application holds notes for users, we need to have a representation of a note in the DB:\n - A note will belong to a single user\n - A note can be archived\n - A note will be composed of a title and text content.\n - A note will have a creation date\n\nLets jump right ahead and create the model:\n```sh\n$ lb model\n? Enter the model name: note\n? Select the datasource to attach note to: db (mssql)\n? Select model's base class PersistedModel\n? Expose note via the REST API? Yes\n? Custom plural form (used to build REST URL):\n? Common model or server only? server\n```\n\nNow lets add its properties:\n```sh\nLet's add some note properties now.\n\nEnter an empty property name when done.\n? Property name: id\n   invoke   loopback:property\n? Property type: string\n? Required? Yes\n? Default value[leave blank for none]:\n\nLet's add another note property.\nEnter an empty property name when done.\n? Property name: username\n   invoke   loopback:property\n? Property type: string\n? Required? Yes\n? Default value[leave blank for none]:\n\nLet's add another note property.\nEnter an empty property name when done.\n? Property name: created\n   invoke   loopback:property\n? Property type: date\n? Required? Yes\n? Default value[leave blank for none]:\n\nLet's add another note property.\nEnter an empty property name when done.\n? Property name: archived\n   invoke   loopback:property\n? Property type: boolean\n? Required? Yes\n? Default value[leave blank for none]: false\n\nLet's add another note property.\nEnter an empty property name when done.\n? Property name: title\n   invoke   loopback:property\n? Property type: string\n? Required? Yes\n? Default value[leave blank for none]:\n\nLet's add another note property.\nEnter an empty property name when done.\n? Property name: content\n   invoke   loopback:property\n? Property type: string\n? Required? Yes\n? Default value[leave blank for none]:\n```\n\nNotice that under `server/models` we have 2 files that represnt the Note model:\n - `note.js` - here we can customize the behaviour of the model\n - `note.json` - JSON description of the model\n\nWe want the `id` field of our model to be the PK and make it auto generated with UUID value. To do that, edit the `server/models/note.json` file by changing the `id` property to be as follows:\n```json\n\"id\": \n{\n    \"type\": \"string\",\n    \"required\": true,\n    \"id\": true,\n    \"generated\": false,\n    \"defaultFn\": \"uuidv4\"\n}\n```\n\nWe want the `created` field of our model to be auto generated with the current timestamp. To do that, edit the `server/models/note.json` file by changing the `created` property to be as follows:\n```json\n\"created\": \n{\n    \"type\": \"date\",\n    \"required\": true,\n    \"defaultFn\": \"now\"\n}\n```\n\nWe want the `username` field of our model to be indexed, as it will be used to find notes of a user. To do that, edit the `server/models/note.json` file by add the `indexes` property as follows:\n```json\n\"indexes\":\n{\n  \"username\": \n  {\n    \"columns\": \"username\"\n  }\n}\n```\n\nNotice also that the `server/model-config.js` file declares our model and connects it to the DB we created earlier.\n\nRestart the server and open the explorer. You will now see the notes model there. You can play around with it by adding, editing and deleting notes. All of the changes you will make will be persisted to the newly created table in the DB.\n\nAdd archive method to the Note model\n====================================\n\nWe want users to be able to archive their notes. We can do that by adding a custom method to the model.\n\nPlease add the following to the exports function in `server/models/note.js`:\n```javascript\n  Note.prototype.archive = function (cb) {\n    var note = this;\n    console.log('archving note', note.id);\n    var delta = {archived: true};\n    note.patchAttributes(delta)\n      .then(function () {\n        return cb();\n      })\n      .catch(function (err) {\n        var errToSend = new Error();\n        errToSend.status = 500;\n        errToSend.message = 'Failed archiving note';\n        return cb(errToSend);\n      });\n  };\n  Note.remoteMethod(\n    'archive',\n    {isStatic: false}\n  );\n```\n\nWe added a method called `archive` to the Note model, which operats on a specific instance of it. When the archive method is called on a note, it will change its `archived` property to true.\n\n\u003e we can achieve the change of the property by using the PATCH method on the Note model directly, but for the sake of this tutorial, we added a custom method for it\n\nOpen the explorer and notice the `archive` method. Try it out on one of your already created notes\n\nExtend user models\n==================\n\nAs mentioned before, loopback comes with predefined models for handling users and authentication.\nFor purposes outside of the scope of this tutrial, we need to extend them.\n\n```sh\n$ lb model\n? Enter the model name: user\n? Select the datasource to attach user to: db (mssql)\n? Select model's base class User\n? Expose user via the REST API? Yes\n? Custom plural form (used to build REST URL):\n? Common model or server only? server\nLet's add some user properties now.\n\nEnter an empty property name when done.\n? Property name: email\n   invoke   loopback:property\n? Property type: string\n? Required? Yes\n? Default value[leave blank for none]:\n```\n\n```sh\n$ lb model\n? Enter the model name: accessToken\n? Select the datasource to attach accessToken to: db (mssql)\n? Select model's base class AccessToken\n? Expose accessToken via the REST API? No\n? Common model or server only? server\nLet's add some accessToken properties now.\n\nEnter an empty property name when done.\n? Property name: id\n   invoke   loopback:property\n? Property type: string\n? Required? Yes\n? Default value[leave blank for none]:\n```\n\nNow lets remove the old model configs from `server/model-config.js` by removing the \"User\" and \"AccessToken\" keys\n\nEdit the `server/models/user.json` file by changing the `email` property to be as follows:\n```json\n\"email\": \n{\n    \"type\": \"string\",\n    \"required\": true,\n    \"id\": true,\n    \"generated\": false\n}\n```\n\nEdit the `server/models/access-token.json` file by changing the `id` property to be as follows:\n```json\n\"id\":\n{\n    \"type\": \"string\",\n    \"required\": true,\n    \"id\": true,\n    \"generated\": false,\n    \"defaultFn\": \"uuidv4\"\n}\n```\n\nChange the authentication process by setting the `server/boot/authentication.js` file to be:\n```javascript\n'use strict';\nvar loopback = require('loopback');\nmodule.exports = function enableAuthentication(app) {\n  app.enableAuth();\n  app.middleware('auth', loopback.token({\n    model: app.models.accessToken,\n    currentUserLiteral: 'me'\n  }));\n};\n```\n\nDefine relations\n================\n\nNow that we have the user and note models, we need to define the relations between them:\n - a user has many notes\n - a note belongs to one user\n\n```sh\n$ lb relation\n? Select the model to create the relationship from: user\n? Relation type: has many\n? Choose a model to create a relationship with: note\n? Enter the property name for the relation: notes\n? Optionally enter a custom foreign key: username\n? Require a through model? No\n```\n\n```sh\n$ lb relation\n? Select the model to create the relationship from: note\n? Relation type: belongs to\n? Choose a model to create a relationship with: user\n? Enter the property name for the relation: user\n? Optionally enter a custom foreign key: username\n```\n\n```sh\n$ lb relation\n? Select the model to create the relationship from: accessToken\n? Relation type: belongs to\n? Choose a model to create a relationship with: user\n? Enter the property name for the relation: user\n? Optionally enter a custom foreign key:\n```\n\n```sh\n$ lb relation\n? Select the model to create the relationship from: user\n? Relation type: has many\n? Choose a model to create a relationship with: accessToken\n? Enter the property name for the relation: accessTokens\n? Optionally enter a custom foreign key:\n? Require a through model? No\n```\n\nCheck out the models json files and notice the added relations section.\n\nOpen the explorer and notice that new resources were added to the note and user models, that describe the relations. For example, a user can directly ask for all of his notes by the `\\users\\:id\\notes` endpoint\n\nDefine ACLs\n===========\n\nOur users privacy is very important to us. As such, we don't want to expose a users notes to other users.\nLoopback comes with build in ACL mechanism to control who can access what resource.\n\nLets define the following ACLs:\n - disable access to all models\n - allow users to write notes\n - allow notes to be editable by their owner only\n - allow notes to be archived by their owner only\n - allow users to get their own user data\n\n```sh\n$ lb acl\n? Select the model to apply the ACL entry to: (all existing models)\n? Select the ACL scope: All methods and properties\n? Select the access type: All (match all types)\n? Select the role All users\n? Select the permission to apply Explicitly deny access\n```\n\n```sh\n$ lb acl\n? Select the model to apply the ACL entry to: user\n? Select the ACL scope: A single method\n? Enter the method name __create__notes\n? Select the role The user owning the object\n? Select the permission to apply Explicitly grant access\n```\n\n```sh\n$ lb acl\n? Select the model to apply the ACL entry to: note\n? Select the ACL scope: All methods and properties\n? Select the access type: Write\n? Select the role The user owning the object\n? Select the permission to apply Explicitly grant access\n```\n\n```sh\n$ lb acl\n? Select the model to apply the ACL entry to: note\n? Select the ACL scope: A single method\n? Enter the method name archive\n? Select the role The user owning the object\n? Select the permission to apply Explicitly grant access\n```\n\n```sh\n$ lb acl\n? Select the model to apply the ACL entry to: user\n? Select the ACL scope: All methods and properties\n? Select the access type: Read\n? Select the role The user owning the object\n? Select the permission to apply Explicitly grant access\n```\n\nCheck out the models json files and notice the added acls section.\n\nAdd the frontend\n================\n\nWe have build our api server, and now it is the time to add the UI part so the users can start enjoying our app.\n\nFirst we need to add support for static files to our server.\n\nPlease remove the `server/boot/root.js` file.\n\nEdit the `files` property of the `server/middleware.json` file:\n```json\n\"files\":\n{\n    \"loopback#static\": {\n        \"params\": \"$!../client\"\n    }\n}\n```\n\nNow copy the sample angular client from the repo to the client directoy in your project.\n\nRestart the server and open http://localhost:3000\nYou will see the site but it is not responsive. The reason is that we need to setup the communication between the UI and the API we have set.\n\nInteractions between the frontend and the backend\n=================================================\n\nLoopback comes with a cli tool that auto generates angular $resource for all your application REST endpoints, allowing to easily interact with your api from angular web applications.\n\nLets generate the javascript code by running:\n```sh\n$ lb-ng server/server.js client/js/services/lb-services.js\n```\n\nYou can checkout the ```client/js/services/lb-services.js``` generated file and see the mappings of all our API endpoints.\n\nNow our controllers can Inject the Note and User moduls and interact with them with native functionality without the need to define the resources outselves.\n\nExample usage for creating a note for a user:\n```javascript\n User.notes\n          .create({id: 'me'}, {\n            title: $scope.note.title,\n            content: $scope.note.content\n          })\n```\n\nRefresh the site and play around in it (first you need to signup)\n\nLogin using Facebook\n====================\n\nUsers prefer to have a quick singup/login process. We can achieve that by allowing users to signup using Facebook\n\nLoopback comes with full support of the Passport middleware.\n\n\u003e First, you will need to setup a facebook app and obtain the app id and secret. Also set the http://localhost:3000 url as a valid url to use the app.\n\nFirst lets install the required dependencies:\n```sh\n$ npm install --save loopback-component-passport\n$ npm install --save passport-facebook\n$ npm install --save cookie-parser\n```\n\nNow lets add to the `server/server.js` file, after the setting of the `app` the following:\n```javascript\nrequire('loopback-component-passport');\nvar cookieParser = require('cookie-parser');\napp.middleware('session:before', cookieParser('cookieSecret'));\n```\n\nAdd `server/boot/providers.json` file that declares the facebook login method:\n```javascript\n{\n  \"facebook-login\": {\n    \"provider\": \"facebook\",\n    \"module\": \"passport-facebook\",\n    \"setAccessToken\": true,\n    \"clientID\": \"**************\",\n    \"clientSecret\": \"****************\",\n    \"callbackURL\": \"http://localhost:3000/api/auth/facebook/callback\",\n    \"authPath\": \"/api/auth/facebook\",\n    \"callbackPath\": \"/api/auth/facebook/callback\",\n    \"successRedirect\": \"/#/my-notes\",\n    \"failureRedirect\": \"/login\",\n    \"scope\": [\n      \"email\"\n    ]\n  }\n}\n```\n\nAdd `server/boot/passport.js`  file that sets up the passport extension:\n```javascript\n'use strict';\n\nmodule.exports = function (app) {\n\n  var loopbackPassport = require('loopback-component-passport');\n  var PassportConfigurator = loopbackPassport.PassportConfigurator;\n  var passportConfigurator = new PassportConfigurator(app);\n  passportConfigurator.init();\n  var providers = require('./providers.json');\n  passportConfigurator.setupModels({\n    userModel: app.models.user,\n    userIdentityModel: app.models.userIdentity,\n    userCredentialModel: app.models.userCredential\n  });\n  for (var s in providers) {\n    var c = providers[s];\n    c.session = c.session !== false;\n    passportConfigurator.configureProvider(s, c);\n  }\n\n};\n```\n\nDefine the passport required models:\n```sh\n$ lb model\n? Enter the model name: userIdentity\n? Select the datasource to attach userIdentity to: db (mssql)\n? Select model's base class (custom)\n? Enter the base model name: UserIdentity\n? Expose userIdentity via the REST API? No\n? Common model or server only? server\n```\n\n```sh\n$ lb model\n? Enter the model name: userCredential\n? Select the datasource to attach userCredential to: db (mssql)\n? Select model's base class (custom)\n? Enter the base model name: UserCredential\n? Expose userCredential via the REST API? No\n? Common model or server only? server\n```\n\nrestart the server and try to login with facebook\n\nSummary\n=======\n\nYou are welcome to extend the application by adding more models and expiremnting with changes in the UI to interact with those models.\n\nAdding Gmail login is as easy as adding another part to the `providers.json` file.\n\nCredits\n=======\n\nThis tutorial is based on the https://loopback.io/doc/en/lb3/Getting-started-with-LoopBack.html official tutorial by Loopback\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpruvonet%2Floopback-getting-started","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpruvonet%2Floopback-getting-started","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpruvonet%2Floopback-getting-started/lists"}