SlideShare a Scribd company logo
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
Ad

More Related Content

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
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
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
Javier Lafora Rey
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.js
TechExeter
 
Vue business first
Vue business firstVue business first
Vue business first
Vitalii Ratyshnyi
 
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
 
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
Katy Slemon
 
Vue js and Vue Material
Vue js and Vue MaterialVue js and Vue Material
Vue js and Vue Material
Eueung Mulyana
 
An Introduction to Vuejs
An Introduction to VuejsAn Introduction to Vuejs
An Introduction to Vuejs
Paddy Lock
 
Vue.js
Vue.jsVue.js
Vue.js
Jadson Santos
 
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
Holly Schinsky
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
Pagepro
 
Love at first Vue
Love at first VueLove at first Vue
Love at first Vue
Dalibor Gogic
 
Building impressive layout systems with vaadin
Building impressive layout systems with vaadinBuilding impressive layout systems with vaadin
Building impressive layout systems with vaadin
Peter Lehto
 
Vaadin with Java EE 7
Vaadin with Java EE 7Vaadin with Java EE 7
Vaadin with Java EE 7
Peter 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 5
Rob Tweed
 
Vue, vue router, vuex
Vue, vue router, vuexVue, vue router, vuex
Vue, vue router, vuex
Samundra khatri
 
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
Takuya 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 thing
Joonas 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.js
Zachary Klein
 
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
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.js
TechExeter
 
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
 
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
Katy Slemon
 
Vue js and Vue Material
Vue js and Vue MaterialVue js and Vue Material
Vue js and Vue Material
Eueung Mulyana
 
An Introduction to Vuejs
An Introduction to VuejsAn Introduction to Vuejs
An Introduction to Vuejs
Paddy 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.js
Holly Schinsky
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
Pagepro
 
Building impressive layout systems with vaadin
Building impressive layout systems with vaadinBuilding impressive layout systems with vaadin
Building impressive layout systems with vaadin
Peter Lehto
 
Vaadin with Java EE 7
Vaadin with Java EE 7Vaadin with Java EE 7
Vaadin with Java EE 7
Peter 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 5
Rob 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.js
Takuya 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 thing
Joonas 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.js
Zachary Klein
 

Viewers also liked (9)

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 Evolution
Dev_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 Bluemix
Dev_Events
 
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 Evolution
Dev_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 Bluemix
Dev_Events
 
Ad

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
Dev_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 angularjs
Erhwen Kuo
 
Backbone js
Backbone jsBackbone js
Backbone js
Rohan Chandane
 
Animation And Testing In AngularJS
Animation And Testing In AngularJSAnimation And Testing In AngularJS
Animation And Testing In AngularJS
Edureka!
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
Knoldus Inc.
 
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
RapidValue
 
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
Drew Morris
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
AgileThought
 
AngularJS.part1
AngularJS.part1AngularJS.part1
AngularJS.part1
Andrey Kolodnitsky
 
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
 
AngularJs Crash Course
AngularJs Crash CourseAngularJs Crash Course
AngularJs Crash Course
Keith Bloomfield
 
ASP.NET MVC Extensibility
ASP.NET MVC ExtensibilityASP.NET MVC Extensibility
ASP.NET MVC Extensibility
Simone Chiaretta
 
Welovejs AngularJS
Welovejs AngularJS Welovejs AngularJS
Welovejs AngularJS
Tomas Corral Casas
 
An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
TO THE NEW Pvt. Ltd.
 
Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciolla
Andrea 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 wae
Bishal Khanal
 
Devise and Rails
Devise and RailsDevise and Rails
Devise and Rails
William Leeper
 
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
Ozeki 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 started
Stéphane Bégaudeau
 
Adding User Management to Node.js
Adding User Management to Node.jsAdding User Management to Node.js
Adding User Management to Node.js
Dev_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 angularjs
Erhwen Kuo
 
Animation And Testing In AngularJS
Animation And Testing In AngularJSAnimation And Testing In AngularJS
Animation And Testing In AngularJS
Edureka!
 
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
RapidValue
 
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
Drew Morris
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
AgileThought
 
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
 
Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciolla
Andrea 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 wae
Bishal 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#.NET
Ozeki 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 started
Stéphane Bégaudeau
 
Ad

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
Dev_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 Microservices
Dev_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 Lab
Dev_Events
 
Introduction to Blockchain and Hyperledger
Introduction to Blockchain and HyperledgerIntroduction to Blockchain and Hyperledger
Introduction to Blockchain and Hyperledger
Dev_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 8
Dev_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 Concierge
Dev_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 View
Dev_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 Servers
Dev_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 Bluemix
Dev_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 Cloud
Dev_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
 
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
Dev_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 Microservices
Dev_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 Lab
Dev_Events
 
Introduction to Blockchain and Hyperledger
Introduction to Blockchain and HyperledgerIntroduction to Blockchain and Hyperledger
Introduction to Blockchain and Hyperledger
Dev_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 8
Dev_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 Concierge
Dev_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 View
Dev_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 Servers
Dev_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 Bluemix
Dev_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 Cloud
Dev_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
 

Recently uploaded (20)

AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Aqusag Technologies
 
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul
 
Cybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure ADCybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure AD
VICTOR MAESTRE RAMIREZ
 
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc
 
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptxDevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
Justin Reock
 
Rusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond SparkRusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond Spark
carlyakerly1
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
Electronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploitElectronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploit
niftliyevhuseyn
 
Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.
hpbmnnxrvb
 
2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx
Samuele Fogagnolo
 
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
organizerofv
 
Heap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and DeletionHeap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and Deletion
Jaydeep Kale
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-UmgebungenHCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
panagenda
 
Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)
Ortus Solutions, Corp
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Aqusag Technologies
 
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul
 
Cybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure ADCybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure AD
VICTOR MAESTRE RAMIREZ
 
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc
 
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptxDevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
Justin Reock
 
Rusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond SparkRusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond Spark
carlyakerly1
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
Electronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploitElectronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploit
niftliyevhuseyn
 
Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.
hpbmnnxrvb
 
2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx
Samuele Fogagnolo
 
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
organizerofv
 
Heap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and DeletionHeap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and Deletion
Jaydeep Kale
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-UmgebungenHCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
panagenda
 
Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)
Ortus Solutions, Corp
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 

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