SlideShare a Scribd company logo
1 of 17
Download to read offline
remkohdev 1/10/2016
QAVideos (2) – Add Custom Model and ORM to Node.js
remkohde.com/2016/01/10/add-custom-objects-and-user-management-to-nodejs-2/
This is part 2 in a series to build a sample application called QAVideos using StrongLoop.
In part 1 ‘QAVideos (Part 1), Adding User Management to Node.js with StrongLoop ‘, I showed how to add User
Management to a Node.js app using StrongLoop.
In this part 2, I will add a custom data model, i.e. a Video, Question and Answer models and use ORM to persist
data to a PostGreSQL database.
Part 3 is found here, which adds model extensions and uses Swagger (now Open API Initiative) support.
Requirements:
Install Node.js and npm,
Install StrongLoop.
Check if the ‘slc’ tool is installed, by running ‘slc’ from the commandline. If not, follow the installation
instructions, here.
Get the source code for part 1 of this tutorial and follow the installation instructions here.
Table of Contents:
1. Create Data Model
2. Define Relation
3. Adding ACL
4. Add Video Functionality
5. Add Data Source
1. Create Data Model
First, test if QAVideos (part 1) is running correctly by typing ‘node .’ in the root directory and browsing to
‘http://localhost:3000/explorer’ in your browser.
Now add a custom model ‘Video’ so that users can manage a list of videos. To do this, I create a model for the
Video, define the relationship between Video and User (a User can have many videos), and specify the access level
of users to the Video object using an Access Control List (ACL).
To create models with StrongLoop you can use the ‘slc loopback:model’ command to run the model generator. I will
create a Video model with the following properties:
title (string; required),
url (string; required),
username (string; not required),
date_published (date; not required),
1/17
likes (number; not required),
dislikes (number; not required).
$ slc loopback:model Video
2/17
This creates two files: ~/common/models/video.js and ~/common/models/video.json. The ‘video.js’ file exports the
video.js module as a function that takes a Video model as a parameter. The ‘video.json’ file is the configuration file
for the Video model.
video.js
module.exports = function(Video) {
};
video.json
{
"name": "Video",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
},
"url": {
"type": "string",
"required": true
},
"username": {
"type": "string"
},
"date_published": {
"type": "date"
},
"likes": {
"type": "number"
},
"dislikes": {
"type": "number"
}
},
3/17
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
2. Define Relation
In this case, I want to create a 1-to-many relation between the User model and the Video model.
There are 3 ways to do this, and I will use the second ‘belongs to’ method for a reason:
1. A ‘has many’ relation from User to Video managed by StrongLoop. The custom foreign key is optional if you
plan to only use the memory database, by default StrongLoop links the two object models User and Video by
a ‘video.userId’ property if you use the ‘has many’ relation.
2. A ‘belongs to’ relation from Video to User managed by StrongLoop, or
3. Custom manage the relation by implementing your own code.
If you plan like I do, to switch to a relational database later (see below), then the StrongLoop automigrate tool at this
moment does not support persisting the foreign key relationship from the ‘has many’ relation, and therefor you must
either use the ‘belongs to’ relation or you need to explicitly define it in our code (for create, update and find ‘My
Videos’). I recommend to and in this tutorial use the ‘belongs to’ relation from Video to User.
To define the relation between User and Video, create a one-to-many relation as follows.
$ slc loopback:relation
This results in the following ‘relations’ configuration in the ‘~/common/models/video.json’ file. You can also directly
add the relation configuration to the ‘~/common/models/video.json’ file (for instance if you get an error with the
generator).
"relations": {
"videoBelongsToUser": {
"type": "belongsTo",
"model": "User",
"foreignKey": "videotouserid"
}
},
3. Adding ACL
To define access control to the video object, I will use StrongLoop’s ACL tool. I want to create the following access
controls:
Deny everyone all endpoints, as the default behavior.
Allow everyone to view videos by adding a ‘READ’ permission.
Allow authenticated users to ‘EXECUTE.create’ videos.
Allow the video owner to edit and thus delete videos by adding a ‘WRITE’ permission.
$ slc loopback:acl
4/17
This results in the modification of the ‘~/common/models/video.json’ file, the acl generator will add the following
lines.
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW",
"property": "create"
},
{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
5/17
}],
Regenerate the Angular Services
With a new model added to our app, from the command-line re-run the ‘lb-ng’ command to add an Angular services
SDK for the models in the QAVideos app.
$ lb-ng server/server.js client/js/lb-ng-services.js
4. Add Video Functionality
Now, we are ready to add the model back into the web functionality and list all videos, list videos by user, add
videos, and edit videos.
Modify the ‘~/client/index.html’ template and add the following list items to the menu, under the logout list item.
Sign up
Log in
Log out
All Videos
My Videos
Add Video
6/17
view raw qavideos-index.html-2.1 hosted with by GitHub
7/17
The ‘Edit Video’ and ‘Delete Video’ functionality is added to each video listed in the ‘My Videos’ page.
Now add the matching pages, states and the video controllers. Modify the app.js Angular client and add the following
states. The ‘all-videos’ state was previously added, but you need to add a controller called ‘AllVideosController’, but
make sure to not create a duplicate definition of state.
.state('my-videos', {
url: '/my-videos',
templateUrl: 'views/my-videos.html',
controller: 'MyVideosController',
authenticate: true
})
.state('add-video', {
url: '/add-video',
templateUrl: 'views/video-form.html',
controller: 'AddVideoController',
authenticate: true
})
.state('edit-video', {
url: '/edit-video/:id',
templateUrl: 'views/video-form.html',
controller: 'EditVideoController',
authenticate: true
})
.state('delete-video', {
url: '/delete-video/:id',
controller: 'DeleteVideoController',
authenticate: true
})
The ‘all-videos’ state was previously already defined, but we need to add a controller object.
.state('all-videos', {
url: '/all-videos',
templateUrl: 'views/all-videos.html',
controller: 'AllVideosController',
authenticate: true
})
Note that in the ‘edit-video’ and ‘delete-video’ states, we also define the video.id in the ‘url’ property that is passed as
a parameter in the ‘edit-video’ and ‘delete-video’ calls as ‘:id’.
In the ‘~/client/views/’ directory add the following pages:
my-videos.html,
video-form.html, and
forbidden.html.
Edit the following views as follows:
all-videos.html
8/17
<section>
<article ng-repeat="v in videos.slice().reverse()">
<header>
<h1>{{v.title}}</h1>
</header>
<p>id: {{v.id}}</p>
<p>url: {{v.url}}</p>
<p>by: {{v.username}}</p>
<p>date published: {{v.date_published}}</p>
<p>likes: {{v.likes}}, dislikes {{v.dislikes}}</p>
</article>
</section>
my-videos.html
<section>
<article ng-repeat="v in videos.slice().reverse()">
<header>
<h1>{{v.title}}</h1>
</header>
<p>id: {{v.id}}</p>
<p>url: {{v.url}}</p>
<p>by: {{v.username}}</p>
<p>date: {{v.date_published}}</p>
<p>likes: {{v.likes}}, dislikes {{v.dislikes}}</p>
<div class="actions" ng-show="currentUser">
<button ui-sref="edit-video({ id: v.id })">Edit</button>
<button a ui-sref="delete-video{ id: v.id })">Delete</button>
</div>
</article>
</section>
Note that the video.id is passed as a parameter in the ‘edit-video’ and ‘delete-video’ function call.
video-form.html
<section>
<form name="form" ng-submit="submitVideo()">
<fieldset>
<legend>{{action}} Video Form</legend>
<div class="form-group">
<label>Title</label>
<input type="text" ng-model="video.title">
</div>
<div class="form-group">
<label>URL</label>
<input type="text" ng-model="video.url">
</div>
<div class="form-group">
9/17
<label>Username</label>
<input type="text" ng-model="video.username">
</div>
<div class="form-group">
<label>Date Published <i>(yyyy-mm-dd)</i></label>
<input type="text" ng-model="video.date_published">
</div>
<div class="form-group">
<label>Likes</label>
<input type="text" ng-model="video.likes">
</div>
<div class="form-group">
<label>Dislikes</label>
<input type="text" ng-model="video.dislikes">
</div>
<div class="actions">
<button>{{action}} video</button>
</div>
</fieldset>
</form>
<section>
forbidden.html
<section>
<article>
<header>
<h1>Forbidden</h1>
</header>
<p>An error occurred.</p>
</article>
</section>
To add the video controller, create a new file ‘~/client/js/controllers/video.js’. Add the ‘AllVideosController’,
‘MyVideosController’, and ‘AddVideoController’ in the ‘~/client/js/controllers/video.js’ file.
angular.module('app')
.controller('AllVideosController', ['$scope', 'Video',
function($scope, Video) {
$scope.videos = Video.find();
}
])
.controller('MyVideosController', ['$scope', 'Video', '$rootScope',
function($scope, Video, $rootScope) {
$scope.videos = Video.find({
filter: {
where: {
/** note: normally we would use just the built-in userId,
* but for the relational db we need to use the foreign key
10/17
'uservideoid' explicitly
userId: $rootScope.currentUser.id
*/
videotouserid: $rootScope.currentUser.id
}
}
});
}
])
.controller('AddVideoController', ['$scope', 'Video', '$state', '$rootScope',
function($scope, Video, $state, $rootScope) {
$scope.action = 'Add';
$scope.video = {};
$scope.isDisabled = false;
$scope.submitVideo = function() {
Video
.create({
title: $scope.video.title,
url: $scope.video.url,
username: $scope.video.username,
date_published: $scope.video.date_published,
likes: $scope.video.likes,
dislikes: $scope.video.dislikes,
userId: $rootScope.currentUser.id,
videotouserid: $rootScope.currentUser.id
})
.$promise
.then(
// onsuccess
function() {
$state.go('all-videos');
},
// onerror
function(err){
}
);
};
}
])
;
Then, add the link to the new script in the ‘index.html’ file, right below the ‘js/controllers/auth.js’ script.
<script src="js/controllers/video.js"></script>
Also add the ‘EditVideoController’ and the ‘DeleteVideoController’ to the ‘video.js’ file.
11/17
.controller('DeleteVideoController', ['$scope', 'Video', '$state', '$stateParams',
function($scope, Video, $state, $stateParams) {
Video
.deleteById({ id: $stateParams.id })
.$promise
.then(function() {
$state.go('my-videos');
});
}
])
.controller('EditVideoController', ['$scope', '$q', 'Video', '$stateParams',
'$state',
function($scope, $q, Video, $stateParams, $state) {
$scope.action = 'Edit';
$scope.video = {};
$scope.isDisabled = true;
$q.all([
Video
.findById({ id: $stateParams.id })
.$promise
])
.then(function(data) {
$scope.video = data[0];
});
$scope.submitVideo = function() {
$scope.video
.$save()
.then(function(video) {
$state.go('all-videos');
},
function(err){
$state.go('forbidden');
});
};
}
])
Test to see if your configuration is running correctly, by running your application from the commandline using ‘node .’
and opening the ‘http://localhost:3000/explorer’ in your browser.
12/17
5. Add Data Source
Instead of using an in-memory database, I want to use a PostGreSQL database for persisted storage, though you
can choose any other data storage supported by StrongLoop. Because we used the default in-memory database so
far, use and video information was lost each time we restarted or stopped the application.
Note: you must have chosen the ‘belongs to’ relation from Video to User in the ‘slc loopback:relation’ command of
the relation generator, cause the ‘has many’ relation at the time of writing this tutorial was not supported in the
automigrate tool.
From the command line, install the database connector, in this case a PostGreSQL connector.
$ npm install --save loopback-connector-postgresql
Install PostGres, either on your local machine or use a remote PostGres installation, and create a new database
‘<db_name>’. On Bluemix there is an ElephantSQL service and a Compose for PostGreSQL service, you can use.
13/17
Generate the data source.
$ slc loopback:datasource postgresdb
Don’t use a hyphen in your name, this is not allowed in StrongLoop. This process creates a new data source
reference in the ‘~/server/datasources.json’ file, with the default memory database and the newly configured
postgresdb connector.
14/17
{
"db": {
"name": "db",
"connector": "memory"
},
"postgresdb": {
"name": "postgresdb",
"connector": "postgresql",
"host": "db.elephantsql.com",
"port": "5432",
"database": "w",
"username": "w",
"password": "passw0rd"
}
}
Now modify the ‘~/server/model-config.json’ file and replace the ‘db’ value for the ‘dataSource’ properties on the
object models by the new ‘postgresdb’ dataSource.
"User": {
"dataSource": "postgresdb"
},
"AccessToken": {
"dataSource": "postgresdb",
"public": false
},
"ACL": {
"dataSource": "postgresdb",
"public": false
},
"RoleMapping": {
"dataSource": "postgresdb",
"public": false
},
"Role": {
"dataSource": "postgresdb",
"public": false
},
"Video": {
"dataSource": "postgresdb"
}
The last thing that remains to do now, is to use the ‘automigrate’ tool in StrongLoop to generate the tables that map
to our data model. Create a new directory ‘~/server/bin/’ and in it, add a new file ‘~/server/bin/automigrate.js’.
var app = require('../server');
var dataSource = app.dataSources.postgresdb;
dataSource.automigrate([
'User',
'AccessToken',
'ACL',
15/17
'RoleMapping',
'Role',
'Video'
], function(err) {
if (err) throw err;
});
To run the automigrate script, execute the following command from the project root.
node server/bin/automigrate.js
Sometimes you get the following error” ‘too many connections for role’ because you are creating too many models at
once. It can help to comment out some models and run the automigrate for two models at a time. Rerun the tool,
commenting out the models that are already created, and uncomment the models to be created.
var app = require('../server');
var dataSource = app.dataSources.postgresdb;
dataSource.automigrate([
'User',
'AccessToken'/**,
'ACL',
'RoleMapping',
'Role',
'Video'*/
], function(err) {
if (err) throw err;
});
Check your PostGres installation to make sure the tables were created successfully.
16/17
Now, start your application again with the ‘node .’ command and in your browser go to http://0.0.0.0:3000.
Now if you sign up with a new user, the user is persisted to the PostGres database. Signing up with the same
username now will display the ‘Forbidden’ state.
To get the source code for QAVideos (part 2) go here.
In Part 3, we will finish the application and extend the built-in User model, add Songs and link the Songs to our
Videos, and add User Groups and Categories to Videos and Songs.
17/17

More Related Content

What's hot

Soft shake 2013 - make use of sonar on your mobile developments
Soft shake 2013 - make use of sonar on your mobile developmentsSoft shake 2013 - make use of sonar on your mobile developments
Soft shake 2013 - make use of sonar on your mobile developmentsrfelden
 
How to create a skeleton of a Java console application
How to create a skeleton of a Java console applicationHow to create a skeleton of a Java console application
How to create a skeleton of a Java console applicationDmitri Pisarenko
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.jsTechExeter
 
Hybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitHybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitAriya Hidayat
 
How to Build ToDo App with Vue 3 + TypeScript
How to Build ToDo App with Vue 3 + TypeScriptHow to Build ToDo App with Vue 3 + TypeScript
How to Build ToDo App with Vue 3 + TypeScriptKaty Slemon
 
Vue js and Vue Material
Vue js and Vue MaterialVue js and Vue Material
Vue js and Vue MaterialEueung Mulyana
 
An Introduction to Vuejs
An Introduction to VuejsAn Introduction to Vuejs
An Introduction to VuejsPaddy Lock
 
The Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.jsThe Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.jsHolly Schinsky
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.jsPagepro
 
Building impressive layout systems with vaadin
Building impressive layout systems with vaadinBuilding impressive layout systems with vaadin
Building impressive layout systems with vaadinPeter Lehto
 
Vaadin with Java EE 7
Vaadin with Java EE 7Vaadin with Java EE 7
Vaadin with Java EE 7Peter Lehto
 
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5Rob Tweed
 
Vue 2.0 + Vuex Router & Vuex at Vue.js
Vue 2.0 + Vuex Router & Vuex at Vue.jsVue 2.0 + Vuex Router & Vuex at Vue.js
Vue 2.0 + Vuex Router & Vuex at Vue.jsTakuya Tejima
 
Vue.js is boring - and that's a good thing
Vue.js is boring - and that's a good thingVue.js is boring - and that's a good thing
Vue.js is boring - and that's a good thingJoonas Lehtonen
 
Room with a Vue - Introduction to Vue.js
Room with a Vue - Introduction to Vue.jsRoom with a Vue - Introduction to Vue.js
Room with a Vue - Introduction to Vue.jsZachary Klein
 

What's hot (20)

Soft shake 2013 - make use of sonar on your mobile developments
Soft shake 2013 - make use of sonar on your mobile developmentsSoft shake 2013 - make use of sonar on your mobile developments
Soft shake 2013 - make use of sonar on your mobile developments
 
How to create a skeleton of a Java console application
How to create a skeleton of a Java console applicationHow to create a skeleton of a Java console application
How to create a skeleton of a Java console application
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.js
 
Vue business first
Vue business firstVue business first
Vue business first
 
Hybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitHybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKit
 
How to Build ToDo App with Vue 3 + TypeScript
How to Build ToDo App with Vue 3 + TypeScriptHow to Build ToDo App with Vue 3 + TypeScript
How to Build ToDo App with Vue 3 + TypeScript
 
Vue js and Vue Material
Vue js and Vue MaterialVue js and Vue Material
Vue js and Vue Material
 
An Introduction to Vuejs
An Introduction to VuejsAn Introduction to Vuejs
An Introduction to Vuejs
 
Vue.js
Vue.jsVue.js
Vue.js
 
The Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.jsThe Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.js
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
 
Love at first Vue
Love at first VueLove at first Vue
Love at first Vue
 
Building impressive layout systems with vaadin
Building impressive layout systems with vaadinBuilding impressive layout systems with vaadin
Building impressive layout systems with vaadin
 
Vaadin with Java EE 7
Vaadin with Java EE 7Vaadin with Java EE 7
Vaadin with Java EE 7
 
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
 
Vue, vue router, vuex
Vue, vue router, vuexVue, vue router, vuex
Vue, vue router, vuex
 
Vue 2.0 + Vuex Router & Vuex at Vue.js
Vue 2.0 + Vuex Router & Vuex at Vue.jsVue 2.0 + Vuex Router & Vuex at Vue.js
Vue 2.0 + Vuex Router & Vuex at Vue.js
 
Vue.js is boring - and that's a good thing
Vue.js is boring - and that's a good thingVue.js is boring - and that's a good thing
Vue.js is boring - and that's a good thing
 
Room with a Vue - Introduction to Vue.js
Room with a Vue - Introduction to Vue.jsRoom with a Vue - Introduction to Vue.js
Room with a Vue - Introduction to Vue.js
 

Viewers also liked

The App Evolution
The App Evolution The App Evolution
The App Evolution Dev_Events
 
Portlet Specification 3.0 Is Here!
Portlet Specification 3.0 Is Here! Portlet Specification 3.0 Is Here!
Portlet Specification 3.0 Is Here! Dev_Events
 
Creating Sentiment Line Chart with Watson
Creating Sentiment Line Chart with Watson Creating Sentiment Line Chart with Watson
Creating Sentiment Line Chart with Watson Dev_Events
 
Building Next Generation Applications and Microservices
Building Next Generation Applications and Microservices Building Next Generation Applications and Microservices
Building Next Generation Applications and Microservices Dev_Events
 
Liberty: The Right Fit for Micro Profile?
Liberty: The Right Fit for Micro Profile?Liberty: The Right Fit for Micro Profile?
Liberty: The Right Fit for Micro Profile?Dev_Events
 
The App Evolution
The App EvolutionThe App Evolution
The App EvolutionDev_Events
 
OpenWhisk - Serverless Architecture
OpenWhisk - Serverless Architecture OpenWhisk - Serverless Architecture
OpenWhisk - Serverless Architecture Dev_Events
 
Building Cognitive Applications with Watson APIs
Building Cognitive Applications with Watson APIs Building Cognitive Applications with Watson APIs
Building Cognitive Applications with Watson APIs Dev_Events
 
Create and Manage APIs with API Connect, Swagger and Bluemix
Create and Manage APIs with API Connect, Swagger and BluemixCreate and Manage APIs with API Connect, Swagger and Bluemix
Create and Manage APIs with API Connect, Swagger and BluemixDev_Events
 

Viewers also liked (9)

The App Evolution
The App Evolution The App Evolution
The App Evolution
 
Portlet Specification 3.0 Is Here!
Portlet Specification 3.0 Is Here! Portlet Specification 3.0 Is Here!
Portlet Specification 3.0 Is Here!
 
Creating Sentiment Line Chart with Watson
Creating Sentiment Line Chart with Watson Creating Sentiment Line Chart with Watson
Creating Sentiment Line Chart with Watson
 
Building Next Generation Applications and Microservices
Building Next Generation Applications and Microservices Building Next Generation Applications and Microservices
Building Next Generation Applications and Microservices
 
Liberty: The Right Fit for Micro Profile?
Liberty: The Right Fit for Micro Profile?Liberty: The Right Fit for Micro Profile?
Liberty: The Right Fit for Micro Profile?
 
The App Evolution
The App EvolutionThe App Evolution
The App Evolution
 
OpenWhisk - Serverless Architecture
OpenWhisk - Serverless Architecture OpenWhisk - Serverless Architecture
OpenWhisk - Serverless Architecture
 
Building Cognitive Applications with Watson APIs
Building Cognitive Applications with Watson APIs Building Cognitive Applications with Watson APIs
Building Cognitive Applications with Watson APIs
 
Create and Manage APIs with API Connect, Swagger and Bluemix
Create and Manage APIs with API Connect, Swagger and BluemixCreate and Manage APIs with API Connect, Swagger and Bluemix
Create and Manage APIs with API Connect, Swagger and Bluemix
 

Similar to Add Custom Model and ORM to Node.js App with StrongLoop

Adding User Management to Node.js
Adding User Management to Node.jsAdding User Management to Node.js
Adding User Management to Node.jsDev_Events
 
Structure your Play application with the cake pattern (and test it)
Structure your Play application with the cake pattern (and test it)Structure your Play application with the cake pattern (and test it)
Structure your Play application with the cake pattern (and test it)yann_s
 
01 startoff angularjs
01 startoff angularjs01 startoff angularjs
01 startoff angularjsErhwen Kuo
 
Animation And Testing In AngularJS
Animation And Testing In AngularJSAnimation And Testing In AngularJS
Animation And Testing In AngularJSEdureka!
 
AngularJS Project Setup step-by- step guide - RapidValue Solutions
AngularJS Project Setup step-by- step guide - RapidValue SolutionsAngularJS Project Setup step-by- step guide - RapidValue Solutions
AngularJS Project Setup step-by- step guide - RapidValue SolutionsRapidValue
 
GDG Atlanta - Angular.js Demo and Workshop
GDG Atlanta - Angular.js Demo and WorkshopGDG Atlanta - Angular.js Demo and Workshop
GDG Atlanta - Angular.js Demo and WorkshopDrew Morris
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For ManagersAgileThought
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdevFrank Rousseau
 
Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciollaAndrea Paciolla
 
Getting started with rails active storage wae
Getting started with rails active storage waeGetting started with rails active storage wae
Getting started with rails active storage waeBishal Khanal
 
How to implement camera recording for USB webcam or IP camera in C#.NET
How to implement camera recording for USB webcam or IP camera in C#.NETHow to implement camera recording for USB webcam or IP camera in C#.NET
How to implement camera recording for USB webcam or IP camera in C#.NETOzeki Informatics Ltd.
 
AngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedAngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedStéphane Bégaudeau
 

Similar to Add Custom Model and ORM to Node.js App with StrongLoop (20)

Adding User Management to Node.js
Adding User Management to Node.jsAdding User Management to Node.js
Adding User Management to Node.js
 
Structure your Play application with the cake pattern (and test it)
Structure your Play application with the cake pattern (and test it)Structure your Play application with the cake pattern (and test it)
Structure your Play application with the cake pattern (and test it)
 
01 startoff angularjs
01 startoff angularjs01 startoff angularjs
01 startoff angularjs
 
Backbone js
Backbone jsBackbone js
Backbone js
 
Animation And Testing In AngularJS
Animation And Testing In AngularJSAnimation And Testing In AngularJS
Animation And Testing In AngularJS
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
AngularJS Project Setup step-by- step guide - RapidValue Solutions
AngularJS Project Setup step-by- step guide - RapidValue SolutionsAngularJS Project Setup step-by- step guide - RapidValue Solutions
AngularJS Project Setup step-by- step guide - RapidValue Solutions
 
GDG Atlanta - Angular.js Demo and Workshop
GDG Atlanta - Angular.js Demo and WorkshopGDG Atlanta - Angular.js Demo and Workshop
GDG Atlanta - Angular.js Demo and Workshop
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
 
AngularJS.part1
AngularJS.part1AngularJS.part1
AngularJS.part1
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
 
AngularJs Crash Course
AngularJs Crash CourseAngularJs Crash Course
AngularJs Crash Course
 
ASP.NET MVC Extensibility
ASP.NET MVC ExtensibilityASP.NET MVC Extensibility
ASP.NET MVC Extensibility
 
Welovejs AngularJS
Welovejs AngularJS Welovejs AngularJS
Welovejs AngularJS
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
 
Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciolla
 
Getting started with rails active storage wae
Getting started with rails active storage waeGetting started with rails active storage wae
Getting started with rails active storage wae
 
Devise and Rails
Devise and RailsDevise and Rails
Devise and Rails
 
How to implement camera recording for USB webcam or IP camera in C#.NET
How to implement camera recording for USB webcam or IP camera in C#.NETHow to implement camera recording for USB webcam or IP camera in C#.NET
How to implement camera recording for USB webcam or IP camera in C#.NET
 
AngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedAngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get started
 

More from Dev_Events

Eclipse OMR: a modern, open-source toolkit for building language runtimes
Eclipse OMR: a modern, open-source toolkit for building language runtimesEclipse OMR: a modern, open-source toolkit for building language runtimes
Eclipse OMR: a modern, open-source toolkit for building language runtimesDev_Events
 
Eclipse MicroProfile: Accelerating the adoption of Java Microservices
Eclipse MicroProfile: Accelerating the adoption of Java MicroservicesEclipse MicroProfile: Accelerating the adoption of Java Microservices
Eclipse MicroProfile: Accelerating the adoption of Java MicroservicesDev_Events
 
From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...
From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...
From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...Dev_Events
 
Blockchain Hyperledger Lab
Blockchain Hyperledger LabBlockchain Hyperledger Lab
Blockchain Hyperledger LabDev_Events
 
Introduction to Blockchain and Hyperledger
Introduction to Blockchain and HyperledgerIntroduction to Blockchain and Hyperledger
Introduction to Blockchain and HyperledgerDev_Events
 
Using GPUs to Achieve Massive Parallelism in Java 8
Using GPUs to Achieve Massive Parallelism in Java 8Using GPUs to Achieve Massive Parallelism in Java 8
Using GPUs to Achieve Massive Parallelism in Java 8Dev_Events
 
Lean and Easy IoT Applications with OSGi and Eclipse Concierge
Lean and Easy IoT Applications with OSGi and Eclipse ConciergeLean and Easy IoT Applications with OSGi and Eclipse Concierge
Lean and Easy IoT Applications with OSGi and Eclipse ConciergeDev_Events
 
Eclipse JDT Embraces Java 9 – An Insider’s View
Eclipse JDT Embraces Java 9 – An Insider’s ViewEclipse JDT Embraces Java 9 – An Insider’s View
Eclipse JDT Embraces Java 9 – An Insider’s ViewDev_Events
 
Node.js – ask us anything!
Node.js – ask us anything! Node.js – ask us anything!
Node.js – ask us anything! Dev_Events
 
Swift on the Server
Swift on the Server Swift on the Server
Swift on the Server Dev_Events
 
Being serverless and Swift... Is that allowed?
Being serverless and Swift... Is that allowed? Being serverless and Swift... Is that allowed?
Being serverless and Swift... Is that allowed? Dev_Events
 
Secrets of building a debuggable runtime: Learn how language implementors sol...
Secrets of building a debuggable runtime: Learn how language implementors sol...Secrets of building a debuggable runtime: Learn how language implementors sol...
Secrets of building a debuggable runtime: Learn how language implementors sol...Dev_Events
 
Tools in Action: Transforming everyday objects with the power of deeplearning...
Tools in Action: Transforming everyday objects with the power of deeplearning...Tools in Action: Transforming everyday objects with the power of deeplearning...
Tools in Action: Transforming everyday objects with the power of deeplearning...Dev_Events
 
Microservices without Servers
Microservices without ServersMicroservices without Servers
Microservices without ServersDev_Events
 
Containers Lab
Containers Lab Containers Lab
Containers Lab Dev_Events
 
OpenWhisk Lab
OpenWhisk Lab OpenWhisk Lab
OpenWhisk Lab Dev_Events
 
Serverless Apps with Open Whisk
Serverless Apps with Open Whisk Serverless Apps with Open Whisk
Serverless Apps with Open Whisk Dev_Events
 
Getting Started with Cloud Foundry on Bluemix
Getting Started with Cloud Foundry on BluemixGetting Started with Cloud Foundry on Bluemix
Getting Started with Cloud Foundry on BluemixDev_Events
 
Mobile, Open Source, & the Drive to the Cloud
Mobile, Open Source, & the Drive to the CloudMobile, Open Source, & the Drive to the Cloud
Mobile, Open Source, & the Drive to the CloudDev_Events
 
IBM Design Thinking & the Bluemix Garage Method
IBM Design Thinking & the Bluemix Garage Method IBM Design Thinking & the Bluemix Garage Method
IBM Design Thinking & the Bluemix Garage Method Dev_Events
 

More from Dev_Events (20)

Eclipse OMR: a modern, open-source toolkit for building language runtimes
Eclipse OMR: a modern, open-source toolkit for building language runtimesEclipse OMR: a modern, open-source toolkit for building language runtimes
Eclipse OMR: a modern, open-source toolkit for building language runtimes
 
Eclipse MicroProfile: Accelerating the adoption of Java Microservices
Eclipse MicroProfile: Accelerating the adoption of Java MicroservicesEclipse MicroProfile: Accelerating the adoption of Java Microservices
Eclipse MicroProfile: Accelerating the adoption of Java Microservices
 
From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...
From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...
From Science Fiction to Science Fact: How AI Will Change Our Approach to Buil...
 
Blockchain Hyperledger Lab
Blockchain Hyperledger LabBlockchain Hyperledger Lab
Blockchain Hyperledger Lab
 
Introduction to Blockchain and Hyperledger
Introduction to Blockchain and HyperledgerIntroduction to Blockchain and Hyperledger
Introduction to Blockchain and Hyperledger
 
Using GPUs to Achieve Massive Parallelism in Java 8
Using GPUs to Achieve Massive Parallelism in Java 8Using GPUs to Achieve Massive Parallelism in Java 8
Using GPUs to Achieve Massive Parallelism in Java 8
 
Lean and Easy IoT Applications with OSGi and Eclipse Concierge
Lean and Easy IoT Applications with OSGi and Eclipse ConciergeLean and Easy IoT Applications with OSGi and Eclipse Concierge
Lean and Easy IoT Applications with OSGi and Eclipse Concierge
 
Eclipse JDT Embraces Java 9 – An Insider’s View
Eclipse JDT Embraces Java 9 – An Insider’s ViewEclipse JDT Embraces Java 9 – An Insider’s View
Eclipse JDT Embraces Java 9 – An Insider’s View
 
Node.js – ask us anything!
Node.js – ask us anything! Node.js – ask us anything!
Node.js – ask us anything!
 
Swift on the Server
Swift on the Server Swift on the Server
Swift on the Server
 
Being serverless and Swift... Is that allowed?
Being serverless and Swift... Is that allowed? Being serverless and Swift... Is that allowed?
Being serverless and Swift... Is that allowed?
 
Secrets of building a debuggable runtime: Learn how language implementors sol...
Secrets of building a debuggable runtime: Learn how language implementors sol...Secrets of building a debuggable runtime: Learn how language implementors sol...
Secrets of building a debuggable runtime: Learn how language implementors sol...
 
Tools in Action: Transforming everyday objects with the power of deeplearning...
Tools in Action: Transforming everyday objects with the power of deeplearning...Tools in Action: Transforming everyday objects with the power of deeplearning...
Tools in Action: Transforming everyday objects with the power of deeplearning...
 
Microservices without Servers
Microservices without ServersMicroservices without Servers
Microservices without Servers
 
Containers Lab
Containers Lab Containers Lab
Containers Lab
 
OpenWhisk Lab
OpenWhisk Lab OpenWhisk Lab
OpenWhisk Lab
 
Serverless Apps with Open Whisk
Serverless Apps with Open Whisk Serverless Apps with Open Whisk
Serverless Apps with Open Whisk
 
Getting Started with Cloud Foundry on Bluemix
Getting Started with Cloud Foundry on BluemixGetting Started with Cloud Foundry on Bluemix
Getting Started with Cloud Foundry on Bluemix
 
Mobile, Open Source, & the Drive to the Cloud
Mobile, Open Source, & the Drive to the CloudMobile, Open Source, & the Drive to the Cloud
Mobile, Open Source, & the Drive to the Cloud
 
IBM Design Thinking & the Bluemix Garage Method
IBM Design Thinking & the Bluemix Garage Method IBM Design Thinking & the Bluemix Garage Method
IBM Design Thinking & the Bluemix Garage Method
 

Recently uploaded

SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsAndrey Dotsenko
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 

Recently uploaded (20)

SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 

Add Custom Model and ORM to Node.js App with StrongLoop

  • 1. remkohdev 1/10/2016 QAVideos (2) – Add Custom Model and ORM to Node.js remkohde.com/2016/01/10/add-custom-objects-and-user-management-to-nodejs-2/ This is part 2 in a series to build a sample application called QAVideos using StrongLoop. In part 1 ‘QAVideos (Part 1), Adding User Management to Node.js with StrongLoop ‘, I showed how to add User Management to a Node.js app using StrongLoop. In this part 2, I will add a custom data model, i.e. a Video, Question and Answer models and use ORM to persist data to a PostGreSQL database. Part 3 is found here, which adds model extensions and uses Swagger (now Open API Initiative) support. Requirements: Install Node.js and npm, Install StrongLoop. Check if the ‘slc’ tool is installed, by running ‘slc’ from the commandline. If not, follow the installation instructions, here. Get the source code for part 1 of this tutorial and follow the installation instructions here. Table of Contents: 1. Create Data Model 2. Define Relation 3. Adding ACL 4. Add Video Functionality 5. Add Data Source 1. Create Data Model First, test if QAVideos (part 1) is running correctly by typing ‘node .’ in the root directory and browsing to ‘http://localhost:3000/explorer’ in your browser. Now add a custom model ‘Video’ so that users can manage a list of videos. To do this, I create a model for the Video, define the relationship between Video and User (a User can have many videos), and specify the access level of users to the Video object using an Access Control List (ACL). To create models with StrongLoop you can use the ‘slc loopback:model’ command to run the model generator. I will create a Video model with the following properties: title (string; required), url (string; required), username (string; not required), date_published (date; not required), 1/17
  • 2. likes (number; not required), dislikes (number; not required). $ slc loopback:model Video 2/17
  • 3. This creates two files: ~/common/models/video.js and ~/common/models/video.json. The ‘video.js’ file exports the video.js module as a function that takes a Video model as a parameter. The ‘video.json’ file is the configuration file for the Video model. video.js module.exports = function(Video) { }; video.json { "name": "Video", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "properties": { "title": { "type": "string", "required": true }, "url": { "type": "string", "required": true }, "username": { "type": "string" }, "date_published": { "type": "date" }, "likes": { "type": "number" }, "dislikes": { "type": "number" } }, 3/17
  • 4. "validations": [], "relations": {}, "acls": [], "methods": {} } 2. Define Relation In this case, I want to create a 1-to-many relation between the User model and the Video model. There are 3 ways to do this, and I will use the second ‘belongs to’ method for a reason: 1. A ‘has many’ relation from User to Video managed by StrongLoop. The custom foreign key is optional if you plan to only use the memory database, by default StrongLoop links the two object models User and Video by a ‘video.userId’ property if you use the ‘has many’ relation. 2. A ‘belongs to’ relation from Video to User managed by StrongLoop, or 3. Custom manage the relation by implementing your own code. If you plan like I do, to switch to a relational database later (see below), then the StrongLoop automigrate tool at this moment does not support persisting the foreign key relationship from the ‘has many’ relation, and therefor you must either use the ‘belongs to’ relation or you need to explicitly define it in our code (for create, update and find ‘My Videos’). I recommend to and in this tutorial use the ‘belongs to’ relation from Video to User. To define the relation between User and Video, create a one-to-many relation as follows. $ slc loopback:relation This results in the following ‘relations’ configuration in the ‘~/common/models/video.json’ file. You can also directly add the relation configuration to the ‘~/common/models/video.json’ file (for instance if you get an error with the generator). "relations": { "videoBelongsToUser": { "type": "belongsTo", "model": "User", "foreignKey": "videotouserid" } }, 3. Adding ACL To define access control to the video object, I will use StrongLoop’s ACL tool. I want to create the following access controls: Deny everyone all endpoints, as the default behavior. Allow everyone to view videos by adding a ‘READ’ permission. Allow authenticated users to ‘EXECUTE.create’ videos. Allow the video owner to edit and thus delete videos by adding a ‘WRITE’ permission. $ slc loopback:acl 4/17
  • 5. This results in the modification of the ‘~/common/models/video.json’ file, the acl generator will add the following lines. "acls": [ { "accessType": "*", "principalType": "ROLE", "principalId": "$everyone", "permission": "DENY" }, { "accessType": "READ", "principalType": "ROLE", "principalId": "$everyone", "permission": "ALLOW" }, { "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW", "property": "create" }, { "accessType": "WRITE", "principalType": "ROLE", "principalId": "$owner", "permission": "ALLOW" 5/17
  • 6. }], Regenerate the Angular Services With a new model added to our app, from the command-line re-run the ‘lb-ng’ command to add an Angular services SDK for the models in the QAVideos app. $ lb-ng server/server.js client/js/lb-ng-services.js 4. Add Video Functionality Now, we are ready to add the model back into the web functionality and list all videos, list videos by user, add videos, and edit videos. Modify the ‘~/client/index.html’ template and add the following list items to the menu, under the logout list item. Sign up Log in Log out All Videos My Videos Add Video 6/17
  • 7. view raw qavideos-index.html-2.1 hosted with by GitHub 7/17
  • 8. The ‘Edit Video’ and ‘Delete Video’ functionality is added to each video listed in the ‘My Videos’ page. Now add the matching pages, states and the video controllers. Modify the app.js Angular client and add the following states. The ‘all-videos’ state was previously added, but you need to add a controller called ‘AllVideosController’, but make sure to not create a duplicate definition of state. .state('my-videos', { url: '/my-videos', templateUrl: 'views/my-videos.html', controller: 'MyVideosController', authenticate: true }) .state('add-video', { url: '/add-video', templateUrl: 'views/video-form.html', controller: 'AddVideoController', authenticate: true }) .state('edit-video', { url: '/edit-video/:id', templateUrl: 'views/video-form.html', controller: 'EditVideoController', authenticate: true }) .state('delete-video', { url: '/delete-video/:id', controller: 'DeleteVideoController', authenticate: true }) The ‘all-videos’ state was previously already defined, but we need to add a controller object. .state('all-videos', { url: '/all-videos', templateUrl: 'views/all-videos.html', controller: 'AllVideosController', authenticate: true }) Note that in the ‘edit-video’ and ‘delete-video’ states, we also define the video.id in the ‘url’ property that is passed as a parameter in the ‘edit-video’ and ‘delete-video’ calls as ‘:id’. In the ‘~/client/views/’ directory add the following pages: my-videos.html, video-form.html, and forbidden.html. Edit the following views as follows: all-videos.html 8/17
  • 9. <section> <article ng-repeat="v in videos.slice().reverse()"> <header> <h1>{{v.title}}</h1> </header> <p>id: {{v.id}}</p> <p>url: {{v.url}}</p> <p>by: {{v.username}}</p> <p>date published: {{v.date_published}}</p> <p>likes: {{v.likes}}, dislikes {{v.dislikes}}</p> </article> </section> my-videos.html <section> <article ng-repeat="v in videos.slice().reverse()"> <header> <h1>{{v.title}}</h1> </header> <p>id: {{v.id}}</p> <p>url: {{v.url}}</p> <p>by: {{v.username}}</p> <p>date: {{v.date_published}}</p> <p>likes: {{v.likes}}, dislikes {{v.dislikes}}</p> <div class="actions" ng-show="currentUser"> <button ui-sref="edit-video({ id: v.id })">Edit</button> <button a ui-sref="delete-video{ id: v.id })">Delete</button> </div> </article> </section> Note that the video.id is passed as a parameter in the ‘edit-video’ and ‘delete-video’ function call. video-form.html <section> <form name="form" ng-submit="submitVideo()"> <fieldset> <legend>{{action}} Video Form</legend> <div class="form-group"> <label>Title</label> <input type="text" ng-model="video.title"> </div> <div class="form-group"> <label>URL</label> <input type="text" ng-model="video.url"> </div> <div class="form-group"> 9/17
  • 10. <label>Username</label> <input type="text" ng-model="video.username"> </div> <div class="form-group"> <label>Date Published <i>(yyyy-mm-dd)</i></label> <input type="text" ng-model="video.date_published"> </div> <div class="form-group"> <label>Likes</label> <input type="text" ng-model="video.likes"> </div> <div class="form-group"> <label>Dislikes</label> <input type="text" ng-model="video.dislikes"> </div> <div class="actions"> <button>{{action}} video</button> </div> </fieldset> </form> <section> forbidden.html <section> <article> <header> <h1>Forbidden</h1> </header> <p>An error occurred.</p> </article> </section> To add the video controller, create a new file ‘~/client/js/controllers/video.js’. Add the ‘AllVideosController’, ‘MyVideosController’, and ‘AddVideoController’ in the ‘~/client/js/controllers/video.js’ file. angular.module('app') .controller('AllVideosController', ['$scope', 'Video', function($scope, Video) { $scope.videos = Video.find(); } ]) .controller('MyVideosController', ['$scope', 'Video', '$rootScope', function($scope, Video, $rootScope) { $scope.videos = Video.find({ filter: { where: { /** note: normally we would use just the built-in userId, * but for the relational db we need to use the foreign key 10/17
  • 11. 'uservideoid' explicitly userId: $rootScope.currentUser.id */ videotouserid: $rootScope.currentUser.id } } }); } ]) .controller('AddVideoController', ['$scope', 'Video', '$state', '$rootScope', function($scope, Video, $state, $rootScope) { $scope.action = 'Add'; $scope.video = {}; $scope.isDisabled = false; $scope.submitVideo = function() { Video .create({ title: $scope.video.title, url: $scope.video.url, username: $scope.video.username, date_published: $scope.video.date_published, likes: $scope.video.likes, dislikes: $scope.video.dislikes, userId: $rootScope.currentUser.id, videotouserid: $rootScope.currentUser.id }) .$promise .then( // onsuccess function() { $state.go('all-videos'); }, // onerror function(err){ } ); }; } ]) ; Then, add the link to the new script in the ‘index.html’ file, right below the ‘js/controllers/auth.js’ script. <script src="js/controllers/video.js"></script> Also add the ‘EditVideoController’ and the ‘DeleteVideoController’ to the ‘video.js’ file. 11/17
  • 12. .controller('DeleteVideoController', ['$scope', 'Video', '$state', '$stateParams', function($scope, Video, $state, $stateParams) { Video .deleteById({ id: $stateParams.id }) .$promise .then(function() { $state.go('my-videos'); }); } ]) .controller('EditVideoController', ['$scope', '$q', 'Video', '$stateParams', '$state', function($scope, $q, Video, $stateParams, $state) { $scope.action = 'Edit'; $scope.video = {}; $scope.isDisabled = true; $q.all([ Video .findById({ id: $stateParams.id }) .$promise ]) .then(function(data) { $scope.video = data[0]; }); $scope.submitVideo = function() { $scope.video .$save() .then(function(video) { $state.go('all-videos'); }, function(err){ $state.go('forbidden'); }); }; } ]) Test to see if your configuration is running correctly, by running your application from the commandline using ‘node .’ and opening the ‘http://localhost:3000/explorer’ in your browser. 12/17
  • 13. 5. Add Data Source Instead of using an in-memory database, I want to use a PostGreSQL database for persisted storage, though you can choose any other data storage supported by StrongLoop. Because we used the default in-memory database so far, use and video information was lost each time we restarted or stopped the application. Note: you must have chosen the ‘belongs to’ relation from Video to User in the ‘slc loopback:relation’ command of the relation generator, cause the ‘has many’ relation at the time of writing this tutorial was not supported in the automigrate tool. From the command line, install the database connector, in this case a PostGreSQL connector. $ npm install --save loopback-connector-postgresql Install PostGres, either on your local machine or use a remote PostGres installation, and create a new database ‘<db_name>’. On Bluemix there is an ElephantSQL service and a Compose for PostGreSQL service, you can use. 13/17
  • 14. Generate the data source. $ slc loopback:datasource postgresdb Don’t use a hyphen in your name, this is not allowed in StrongLoop. This process creates a new data source reference in the ‘~/server/datasources.json’ file, with the default memory database and the newly configured postgresdb connector. 14/17
  • 15. { "db": { "name": "db", "connector": "memory" }, "postgresdb": { "name": "postgresdb", "connector": "postgresql", "host": "db.elephantsql.com", "port": "5432", "database": "w", "username": "w", "password": "passw0rd" } } Now modify the ‘~/server/model-config.json’ file and replace the ‘db’ value for the ‘dataSource’ properties on the object models by the new ‘postgresdb’ dataSource. "User": { "dataSource": "postgresdb" }, "AccessToken": { "dataSource": "postgresdb", "public": false }, "ACL": { "dataSource": "postgresdb", "public": false }, "RoleMapping": { "dataSource": "postgresdb", "public": false }, "Role": { "dataSource": "postgresdb", "public": false }, "Video": { "dataSource": "postgresdb" } The last thing that remains to do now, is to use the ‘automigrate’ tool in StrongLoop to generate the tables that map to our data model. Create a new directory ‘~/server/bin/’ and in it, add a new file ‘~/server/bin/automigrate.js’. var app = require('../server'); var dataSource = app.dataSources.postgresdb; dataSource.automigrate([ 'User', 'AccessToken', 'ACL', 15/17
  • 16. 'RoleMapping', 'Role', 'Video' ], function(err) { if (err) throw err; }); To run the automigrate script, execute the following command from the project root. node server/bin/automigrate.js Sometimes you get the following error” ‘too many connections for role’ because you are creating too many models at once. It can help to comment out some models and run the automigrate for two models at a time. Rerun the tool, commenting out the models that are already created, and uncomment the models to be created. var app = require('../server'); var dataSource = app.dataSources.postgresdb; dataSource.automigrate([ 'User', 'AccessToken'/**, 'ACL', 'RoleMapping', 'Role', 'Video'*/ ], function(err) { if (err) throw err; }); Check your PostGres installation to make sure the tables were created successfully. 16/17
  • 17. Now, start your application again with the ‘node .’ command and in your browser go to http://0.0.0.0:3000. Now if you sign up with a new user, the user is persisted to the PostGres database. Signing up with the same username now will display the ‘Forbidden’ state. To get the source code for QAVideos (part 2) go here. In Part 3, we will finish the application and extend the built-in User model, add Songs and link the Songs to our Videos, and add User Groups and Categories to Videos and Songs. 17/17