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 developments
rfelden
 
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
Dmitri Pisarenko
 
Hybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKitHybrid Apps (Native + Web) via QtWebKit
Hybrid Apps (Native + Web) via QtWebKit
Ariya Hidayat
 

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

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

Animation And Testing In AngularJS
Animation And Testing In AngularJSAnimation And Testing In AngularJS
Animation And Testing In AngularJS
Edureka!
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
Frank Rousseau
 

Similar to Add Custom Model and ORM to Node.js (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

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

Recently uploaded (20)

A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 

Add Custom Model and ORM to Node.js

  • 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