Getting started with deepstreamHub is easy and takes less than ten minutes. However, if you have any questions, please get in touch.
This getting started guide will walk you through integrating deepstream in AngularJS (v1.x). You will also learn how to implement the three deepstream core concepts: Records, Events and RPCs.
First, let's start by creating a free deepstreamHub account:
Create a free account and get your API key
deepstream provides a JavaScript library which helps in interacting with your deepstreamHub server.
Connect to deepstreamHub and log in
Create a new app and include Angular as well as the JS-client library:
<!-- deepstream client library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/deepstream.io-client-js/2.1.1/deepstream.js"></script>
<!-- Angular -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
<!-- app -->
<script src="js/app.js"></script>
Get your app url from the dashboard and establish a connection to deepstreamHub using an Angular service:
.service('ds', function() {
/************************************
* Connect and login to deepstreamHub
************************************/
//establish a connection. You can find your endpoint url in the
//deepstreamhub dashbo
return deepstream( '<YOUR APP URL>' ).login();
})
The login
method can take credentials if authentication is configured.
Angular Digest Cycle
In Angular, the digest cycle is automated in a typical Angular environemnt. But, once you perform an action outside the environemnt, mostly async actions, the digest cycle doesn't account for that. You can manually notify the cycle by calling $scope.$digest()
:
angular.module('app', [])
.service('scopeApply', function(){
return function (scope) {
if( !scope.$$phase ) {
scope.$apply();
}
}
})
The scopeApply
service can be re-used anywhere in our application anytime we need to manually call $scope.$apply()
which will eventually call $scope.$digest()
.
Records (realtime datastore)
Records are the documents in deepstreamHub’s realtime datastore. A record is identified by a unique id and can contain any kind of JSON data. Clients and backend processes can create, read, write, update and observe the entire record as well as paths within it. Any change is immediately synchronized amongst all connected subscribers.
Records can be arranged in lists and collections and can contain references to other records to allow for the modelling of relational data structures.
You can learn more about records in the records tutorial.
var myRecord = ds.record.getRecord( 'test/johndoe' );
Values can be stored using the .set()
method
myRecord.set({
firstname: 'John',
lastname: 'Doe'
});
Let's set up two-way bindings with an input field - whenever a path within our record, e.g. firstname
changes we want to update the input. Whenever a user types, we want to update the record.
We can save keystrokes by creating a service that will be responsible for setting values and subscribing to its changes via the $scope object:
angular.module('app', [])
// . . .
.service( 'bindFields', function(scopeApply){
/**
* Bind fields to
* scope and keep scope synced
* with realtime changes
*/
return function getField( scope, record, names ) {
angular.forEach( names, function( name ){
Object.defineProperty( scope, name, {
get: function() {
return record.get( name );
},
set: function( newValue ) {
if( newValue === undefined ) {
return;
}
record.set( name, newValue );
}
});
});
record.subscribe(function() {
scopeApply(scope)
});
}
})
Notice how the record.subscribe
method which is an async method calls the scopeApply
function so as to manually initiate the digest cycle.
It now becomes easier to use in the component:
angular.module('app', [])
// . . .
.component('record', {
template: `
<div class="group realtimedb">
. . .
</div>
`,
controller: function RecordController($scope, ds, bindFields) {
var fields = ['firstname', 'lastname'];
this.record = ds.record.getRecord('test/johndoe');
bindFields($scope, this.record, fields);
}
})
<record></record>
Events (publish-subscribe)
Events are deepstreamHub’s publish-subscribe mechanism. Clients and backend processes can subscribe to event-names (sometimes also called “topics” or “channels”) and receive messages published by other endpoints.
Events are non-persistent, one-off messages. For persistent data, please use records.
Clients and backend processes can receive events using .subscribe()
ds.event.subscribe( 'test-event', function( eventData ){ /*do stuff*/ });
... and publish events using .emit()
ds.event.emit( 'test-event', {some: 'data'} );
A little example:
angular.module('app', [])
// . . .
.component('events', {
template: `
<div class="group pubsub">
. . .
</div>
`,
controller: function EventsController($scope, ds, scopeApply) {
$scope.value = ''
$scope.eventsReceived = [];
// Whenever the user clicks the button
$scope.handleClick = function(){
// Publish an event called `test-event` and send
ds.event.emit('event-data', $scope.value);
};
ds.event.subscribe('event-data', function(val) {
// Whenever we receive a message for this event,
// append a list item to our
$scope.eventsReceived.push(val)
// call $scope.$apply()
scopeApply($scope)
});
}
})
An event is published anytime the button is clicked and the event.subscribe
method listens for this publish action. $scope.$apply
is called to keep things in sync as usual.
RPCs (request-response)
Remote Procedure Calls are deepstreamHub’s request-response mechanism. Clients and backend processes can register as “providers” for a given RPC, identified by a unique name. Other endpoints can request said RPC.
deepstreamHub will route requests to the right provider, load-balance between multiple providers for the same RPC, and handle data-serialisation and transport.
You can make a request using .make()
ds.rpc.make( 'multiply-numbers', { a: 6, b: 7 }, function( err, result ){
//result === 42
});
and answer it using .provide()
ds.rpc.provide( 'multiply-numbers', function( data, response ){
resp.send( data.a * data.b );
});
For example:
angular.module('app', [])
// . . .
.component('rpc', {
template: `
<div class="group reqres">
. . .
</div>
`,
controller: function RPCController($scope, ds, scopeApply) {
$scope.requestValue = '3';
$scope.responseValue = '7';
$scope.displayResponse = '-';
$scope.handleClick = function() {
var data = {
value: parseFloat( $scope.requestValue )
};
// Make a request for `multiply-number` with our data object
// and wait for the response
ds.rpc.make( 'multiply-number', data, function( err, resp ){
//display the response (or an error)
$scope.displayResponse = resp || err.toString();
scopeApply($scope)
});
// Register as a provider for multiply-number
ds.rpc.provide( 'multiply-number', function( data, response ){
// respond to the request by multiplying the incoming number
// with the one from the response input
response.send( data.value * parseFloat( $scope.responseValue ) );
});
}
}
});
Clicking the button makes a request using rpc.make
. The .make
method is also responsible for handling the responses when they come back from the response provider which is the rpc.provide
method.
Where to go next?
To learn how to use deepstreamHub with other frontend frameworks head over to the tutorial section. To learn how to use the JavaScript SDK with NodeJS rather than in a browser, head over to the getting started with NodeJS tutorial.