{ DreamFactory: 'Blog' }

Virtual Relationships Between Database Tables

Posted by Lee Hicks

Thu, Feb 4, 2016

A few years ago, we wrote a blog post about working with related data using the DreamFactory REST API platform. The SQL DB service can return a table along with any number of related tables in a single API call. You can POST the JSON document back to the API in order to create, update, or delete the related data. DreamFactory automatically maintains all of the links and junction objects for you. This is possible because we use the schema information stored in the database to learn about the various relationships between the tables.

But what if you have tables that are related in some way, and those relationships are not detailed in the schema? What if the related tables exist on different databases, or even on different servers? Previously, the only solution was to make multiple calls from the client-side and assemble the information as needed. Or you could create a custom service or write a server-side script to combine unrelated tables of data. Here is a recent blog post about how to do this.

But now, with the introduction of DreamFactory 2.0, developers can specify virtual relationships between any two tables. The tables can be located on different databases, and the databases can be located on different servers. This is a game changing capability because now a developer can create, read, update, and delete related data across multiple servers with a single REST API call!

These virtual relationships are imposed from outside by the DreamFactory platform. There is no need to change the actual configuration of your database. What's more, once DreamFactory knows about the virtual relationships, you can work with related data of any kind without the need for server-side scripting. This feature packs even more capability into the request URL string itself, further reducing the need to build custom REST APIs.

The Magic of DataMesh

We call this revolutionary new feature DataMesh. The SQL DB service now combines the existing relationships found in the schema with any number of virtual relationships specified by the developer. These virtual foreign keys are stored locally as part of the system information maintained by the DreamFactory platform. So now, developers can provision virtual relationships between any of the database services found on the platform. The REST API normalizes database access, so for example, you could combine information from SQL Server, Oracle, and MySQL seamlessly. These new virtual relationships are then exposed in the table records API and invoked with a simple URL parameter just like the other existing relationships.

Slide1-2.jpg

An Inter-Service Example

Say for example that you have a legacy SQL database that contains a contact table with fields like email, name, phone, etc. Over the years a customer table was also created to store purchase information, and these records include a username field which is in the form of an email address. The schema for this database doesn't reflect any relationships between these tables. This might be for historical reasons, or perhaps the developer does not have permission to modify the schema. At any rate, now you need to create an application which displays information from the contact table along with the related purchase history from the customer table.

Using DataMesh, you could create a virtual relationship between the contact table and the customer table by taking the following steps:

    • In the Admin Console you need to provision a service for the legacy database, for this example let's use the name mysql
    • Choose your primary table to access the data, in this case the contact table
    • Select the customer table via the Schema Tab in the Admin Console

schema_table_copy.png

    • Select the username field, and open for edit
    • Click the Foreign Key and Virtual Foreign Key checkboxes, this will enable the Reference Table and Field selection menus below
    • Select contact for the table and email for the field
    • Then click the Update button to save the configuration

schema_field_copy.png

This will automatically create a belongs_to relationship from the customer to the contact, as well as a has_many relationship in the opposite direction. That's it! You have created a virtual relationship.

schema_relationship_copy.png

To see how these different relationships are represented or provisioned via the API, take a look at the following JSON snippets...

As a baseline, here is a field with a foreign key that is part of the existing database schema. In this case our contact table points to a business account table:
    {
        "name": "account_id",
        "type": "reference",
        "is_virtual_foreign_key": false,
        "is_foreign_service": false,
        "ref_service": null,
        "ref_table": "account",
        "ref_fields": "id"
    }
    
Here is the customer field username with a virtual foreign key pointing to the contact table on the same database:
    {
        "name": "username",
        "type": "string",
        "is_virtual_foreign_key": true,
        "is_foreign_service": false,
        "ref_service": null,
        "ref_table": "contact",
        "ref_fields": "email"
    }
    
The relationship from the perspective of the contact table:
    {
        "name": "customer_by_username",
        "type": "has_many",
        "field": "email",
        "is_virtual": true,
        "is_foreign_service": false,
        "ref_service": null,
        "ref_table": "customer",
        "ref_fields": "username"
    }
    
The relationship from the perspective of the customer table:
    {
        "name": "contact_by_username",
        "type": "belongs_to",
        "field": "username",
        "is_virtual": true,
        "is_foreign_service": false,
        "ref_service": null,
        "ref_table": "contact",
        "ref_fields": "email"
    }
    

A Service-to-Service Example

Let's work with the above example again, except this time assume that the customer table exists on a separate database. The only real differences in provisioning are as follows:

  • In the Admin Console you need to provision an additional service for the separate database that contains the customer table, for this example let's use the name ecommerce
  • After clicking the Foreign Key and Virtual Foreign Key checkboxes, you also need to click the Foreign Reference Service checkbox which allows you to select the additional database

schema_field2_copy.png

Take note of the "dot" notation in the relationship name. This indicates that the relationship is pointing to a different service.

schema_relationship2_copy.png

And here are the JSON snippets for this scenario...

Here is the customer field username with a virtual foreign key pointing to the contact table on the legacy database we named mysql:
    {
        "name": "username",
        "type": "string",
        "is_foreign_key": true,
        "is_virtual_foreign_key": true,
        "is_foreign_service": true,
        "ref_service": "legacy",
        "ref_table": "contact",
        "ref_fields": "email"
    }
    
The relationship from the perspective of the contact table:
    {
        "name": "ecommerce.customer_by_username",
        "type": "has_many",
        "field": "email",
        "is_virtual": true,
        "is_foreign_service": true,
        "ref_service": "ecommerce",
        "ref_table": "customer",
        "ref_fields": "username"
    }
    
The relationship from the perspective of the customer table:
    {
        "name": "mysql.contact_by_username",
        "type": "belongs_to",
        "field": "username",
        "is_virtual": true,
        "is_foreign_service": true,
        "ref_service": "mysql",
        "ref_table": "contact",
        "ref_fields": "email"
    }
    

Note that the names of the relationships are auto-generated, but can be aliased to something more desirable for use in the API if needed.

Using the Virtual Relationships

Any of the created relationships can be used in the "related" URL query parameter, just like existing relationships defined by the schema. The beauty of this is that an administrator can provision the databases and create the virtual relationships and then a client developer can simply use the related data given their role based access settings. No custom services needed. In our demo data, the contact "Jane Smith" was found in the customer table with the same email.

http://demo/api/v2/mysql/_table/contact?related=customer_by_username
{
    "resource": [
        {
            "id": 1,
            "first_name": "John",
            "last_name": "Smith",
            "display_name": "John Smith",
            "email": "johnsmith@demo.com",
            "customer_by_username": []
        },
        {
            "id": 2,
            "first_name": "Jane",
            "last_name": "Smith",
            "display_name": "Jane Smith",
            "email": "janesmith@demo.com",
            "customer_by_username": [
                {
                    "id": 2,
                    "display_name": "Jane Elise Smith",
                    "username": "janesmith@demo.com"
                }
            ]
        }
    ]
}

In Conclusion

There are some very powerful capabilities in DreamFactory 2.0 for combining multiple data sources. The new DataMesh feature takes our product one step further in getting all the data your application needs in a easy, clean, and succinct manner. That plus all of the other great features of DreamFactory should get you well on your way to happy app development!

New DreamFactory 2.0 Tutorial Videos

Posted by Ben Busse

Wed, Jan 27, 2016

A lot of people like watching quick screencasts to get up to speed on the basics of a product. We just made a bunch of videos on DreamFactory 2.0 Admin Console that introduce the basics of the product. We've also made some new videos on how to use DreamFactory 2.0 for common use cases, including how to use the REST API for MySQL, SQLite, MongoDB, and remote web services. Stay tuned for more videos over the coming weeks, including how to add custom services, use event scripts, connect multiple databases with our new "data mesh" feature, connect to any SOAP service with REST, and more!

Here are some good videos to check out. Head on over to the DreamFactory docs for a full list of videos.

We'll be adding more videos soon, so check back often. And feel free to let us know what types of videos you'd like to see in the comments. Or make a video tutorial yourself and let us know, so we can post it on the DreamFactory YouTube Channel!

DreamFactory 2.0 Overview

 
How to Use the Services Tab
 
 
 How to Use the API Docs Tab
 
 
How to Use the Scripts Tab
 
 
How to Use the MySQL REST API
 
 
How to Use the MongoDB REST API
 
 

NoSQL DreamFactory API SQL

Wordpress integration with the DreamFactory REST API platform for user management

Posted by Jessica Rose

Thu, Jan 21, 2016

Noggle CEO Lars von Thienen has been indulging in a little island hopping lately. There aren’t many palm trees on these islands, but getting between them is a lot faster and safer now.

The new Noggle service allows controlled and privileged access across a broad information archipelago. It’s a peer-to-peer document network that’s fully indexed and searchable from a single, unified access point.

It’s a clever solution to distributed content that allows users to maintain their own islands of content, without having to store them in shared repositories. They simply tell Noggle where the docs are on their computer and they are exposed to their peer network; fully indexed, searchable, and sharable.

An early challenge for Noggle was how to control shared file access in a secure and extensible way. DreamFactory ended up being the perfect solution. It gave von Thienen a granular system of user creation and role control that’s secure and scalable.

DreamFactory also served as a perfect bolt-on service to their existing Wordpress/Woocommerce portal. Von Thienen crafted a series of functions for integrating DreamFactory into Wordpress and has generously shared them with us.

This code assumes the existence of an existing DreamFactory instance along with a valid user with sufficient priveleges to access the user/system API.

General usage

  1. Get a valid token with GetDreamfactoryToken()
  2. Prepare the POST/GET body with parameters
  3. Call CallDFAPI() with parameters
  4. Define the correct Wordpress hooks and execution points

Get a valid Dreamfactory token

Here is a low level PHP function to get a current token for user management, via a Dreamfactory instance at YOURHOST. It returns a valid token for further processing or error.

function GetDreamfactoryToken()
{
$method = 'POST';
$url = 'https://[YOURHOST.COM]/api/v2/user/session';

$params = array(
'email' => '[YOUREMAIL@HOST.COM]’,
'password' => [USERPW]'
);

// JSON REQUEST FORMAT
$headers = array(
'Accept: application/json',
'Content-Type: application/json'
);

$curl = curl_init();

curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));

curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_SSLVERSION, 4);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($curl);

$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

curl_close($curl);

if ($code == 200) {
$response = json_decode($response, true);
$token= $response["session_token"];
return $token;
} else {
//return $curl_error;
return 'error';
}
}

Call the DreamFactory API

This function is a general helper to call Dreamfactory REST APIs. It returns a decoded JSON string or error code and needs a valid session token and DreamFactory API key.

function CallDFAPI($method, $url, $token, $params, $postbody=NULL)
{
$headers = array(
'X-DreamFactory-Session-Token:'.$token,
'X-DreamFactory-Api-Key: [YOUR_DREAMFACTORY_API_KEY]',
'Accept: application/json',
'Content-Type: application/json'
);

$curl = curl_init();

switch($method) {
case 'GET':
$url .= '?' . http_build_query($params);
break;
case 'POST':
curl_setopt($curl, CURLOPT_POST, true);
if ($postbody != NULL)
curl_setopt($curl, CURLOPT_POSTFIELDS, $postbody);
else
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
break;
case 'PUT':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
break;
case 'PATCH':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH');
if ($postbody != NULL)
curl_setopt($curl, CURLOPT_POSTFIELDS, $postbody);
else
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
break;
case 'DELETE':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
$url .= '?' . http_build_query($params);
break;
}

curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_SSLVERSION, 4);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($curl);

$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

curl_close($curl);

if ($code == 200) {
$response = json_decode($response, true);
return $response;
} else {
//return $response;
return 'error';
}
}

Check if a user is already registered

//Get a session token to perform database actions for users
$token= GetDreamfactoryToken();

$usertocheck=$email; // $email= the email address to check against

$method = 'GET';
$url = 'https://[YOURHOST.COM]/api/v2/system/user';
$params = array(
'filter' => 'email="'.$usertocheck.'"',
'related' => 'user_to_app_to_role_by_user_id'
);

$response=CallDFAPI($method, $url, $token, $params);

if ($response!=‘error')
{
// process response Array
$regemail= $response["resource"][0]["email"];
if ($regemail == $usertocheck)
{
//Yes, user is already in dreamfactory host, now do something…
}
}

Register/Invite a new user via Dreamfactory

//Get a session token to perform database actions for users
$token= GetDreamfactoryToken();

//[SET USER PARAMETER]
$username= …
$email= …
$phone= …
$roleid= …

$method = 'POST';
$url = 'https://[YOUHOST.COM]/api/v2/system/user?send_invite=true';

//Set the correct POST data
$postbody='{ "resource": [ { "name": "'.$username.'", "email": "'.$email.'", "phone": "'.$invitedby.'", "is_active": true, "user_to_app_to_role_by_user_id": [ { "app_id": [YOURAPPID], "role_id": '.$roleid.' } ] } ] }';

$response=CallDFAPI($method, $url, $token, NULL, $postbody);

if ($response!='error')
{
// Success, user has been invited via DF host
//do something…
}

With these tools in place, von Thienen now has an effective solution for user management. But, the integration lets Wordpress and Woocommerce handle the front-end presentation layer and transaction.

Pretty impressive, and much better than that guy who tried to build the first wireless telegraph system for some islands back in 1899.

 

Benchmarking DreamFactory 2.0

Posted by Ben Busse

Mon, Jan 18, 2016

One of the main design goals for DreamFactory 2.0 was to increase speed and scalability. The entire platform was rewritten in the Laravel framework and we adopted JSON Web Tokens (JWT) for better security and completely stateless operation. This post presents some benchmarking results designed to help enterprise customers scale their DreamFactory installation to any desired level of performance. The sections below talk about vertical and horizontal scalability and then we look at the effect of increasing concurrent users.

Vertical Scalability

Below are some results that show the vertical scalability of a single DreamFactory Instance calculated with Apache Benchmark. Two different Amazon Web Services EC2 servers were tested: a m4.2xlarge with 8 processors and a m4.4xlarge with 16 processors.

For this test, we conducted 1000 GET operations from the DreamFactory REST API. There were 100 concurrent users making the requests. Each operation searched, sorted, and retrieved 1000 records from a SQL database. This test was designed to exercise the server side processing required for a complex REST API call.

pic1-1.png

Looking at the two servers, we see a nice doubling of capacity that matches the extra processors and memory. This really shows the vertical scalability of the system. The complex GET scenario highlights the advantages of the additional processor power. 

Next, we tried a similar test with a simple GET command that basically just returned a single database record 5000 times. There were 100 concurrent users making the requests. In this situation, the fixed costs of Internet bandwidth, network switching, and file storage start to take over and the additional processors contribute less.
 pic2-1.png

Look at these results for 5000 simple GETs from the API. As you can see, performance does not fully double with additional processors. This demonstrates the diminishing returns of adding processors without scaling up other fixed assets. By the way, we also looked at POST and DELETE transactions. The results were pretty much what you would expect and in line with the GET requests tested above.

Horizontal Scalability

Below are some results that show the horizontal scalability of a single DreamFactory Instance calculated with Apache Benchmark. Four m4.xlarge Amazon Web Services EC2 instances were configured behind a load balancer. The servers were configured with a common default database and EBS storage.

First we tested the complex GET scenario. The load balanced m4.xlarge servers ran at about the same speed as the m4.4xlarge server tested earlier. This makes sense because each setup had similar CPU and memory installed. Since this example was bound by processing requirements, there was not much advantage to horizontal scaling.

Next, we tested the simple GET scenario. In this case there appears to be some advantage to horizontal scaling. This is probably due to improvements in network IO and the relaxation of other fixed constraints compared to the vertical scalability test.

Concurrent Users

We also evaluated the effects of concurrent users simultaneously calling REST API services on the platform. This test used the complex GET scenario where 1000 records were searched, sorted, and retrieved. The test was conducted with three different Amazon Web Services EC2 instances. The servers were m4.xlarge, m4.2xlarge, and m4.4xlarge. We started with 20 concurrent users and scaled up to 240 simultaneous requests.

The minimum time for the first requests to finish was always around 300 milliseconds. This is because some requests are executed immediately within the minimum time while others must wait to be executed.

The maximum time for the last request to finish will usually increase with the total number of concurrent users. Based on the processor size, the maximum time for the last request can increase sharply past some critical threshold. This is illustrated by the 8 processor example, where maximum request times spike past 160 concurrent users.

The 16 processor server never experienced any degradation of performance all the way to 240 concurrent users. This is the maximum number of concurrent users supported by the Apache Bench test program. Even then, the worst round trip delay was less than ½ second.

A typical mobile application only makes a handful of REST API calls every minute. A situation where hundreds of users are simultaneously calling the backend would be quite unusual, so these benchmarks represent a worst case scenario. The 16 processor server tested below could probably support 100,000 registered users, where 10,000 would be using the app at any given time, and 1000 were making REST API calls during any given time frame.

Profile Results

We have also profiled the code base extensively. We used the Blackfire profiling tools as well. Check these out if you get a chance. Below is a picture of the smooth use of time across all of the different classes and code in the stack. Extensive analysis has demonstrated that there are no "bottlenecks" in the platform.

Scalability Use Cases

Adding multiple processors is a very easy way to implement vertical scalability. As our results show, doubling the number of processors will just about double performance, except for installations that are network I/O or storage bound.

Adding new servers behind a load balancer is a sure fire way to implement horizontal scalability. Because DreamFactory is completely stateless, you can add as many instances as needed and balance them any way you want. Just be sure that they all use the same default database and cache. Other than that, DreamFactory can be scaled just like any other simple LAMP stack.

DreamFactory 2.0 has also been designed to run with or without shared storage. This capability allows DreamFactory to operate perfectly on Platform as a Service (PaaS) systems like Heroku or BlueMix where persistent storage is not available. Scalability becomes even easier to calculate on PaaS, because you can simply specify the number of instances needed.

DreamFactory 2.0 can also be run in a Docker container managed by Kubernetes, Swarm, Fleet, or Mesos. This is a real game changer. We will discuss DreamFactory on Docker and the use of Microservices in more detail in an upcoming blog post. Meanwhile, there is more detailed information about DreamFactory scalability available.

Built For Speed 

We put a lot of work into making DreamFactory 2.0 highly scalable. The use of JSON Web Tokens and the complete rewrite in Laravel contributed greatly to the stateless and highly efficient architecture of the platform. The routing engine, eventing system, and caching options in Laravel are truly state of the art and were directly leveraged by the architecture.

In my tests and benchmarking, there were never strange unexplained delays or other performance characteristics that did not respond in a very scalable manner. Want to double performance? Just double processors, servers, instances, or containers. You will need to get a sense of the load imposed by your application. But, once you do, DreamFactory 2.0 will scale to any desired level of performance.

 

DreamFactory 2.0

Building an AngularJS application using the DreamFactory REST API backend

Posted by Andy Rai

Tue, Jan 5, 2016

angular.png

Now that DreamFactory 2.0 is live on Bitnami, our team has been busy building example apps to show how easy it is to use DreamFactory as your REST API backend. I’ve been working on DreamFactory’s AngularJS sample address book app and tutorial. This blog summarizes a few important things to know about getting started with the DreamFactory Angular sample app.

Background

The DreamFactory Angular sample app is basically a boilerplate application for getting started with DreamFactory. It contains all the setup required to communicate with your DreamFactory installation.

The code is very modular. Each functional piece of the application is a separate Angular module with its own services and directives. For example, there’s a login module with its own folder of templates, services, controllers, interceptors, etc. This module also takes care of managing session tokens and almost everything else related to authentication.

After following the configuration steps in the README, all that’s left is to make HTTP API calls with Angular’s $http service or $resource factory. Here are a few examples and a quick breakdown of sample application

Code Examples

Let’s first start with the app configuration. Using constants and interceptors for configuring API calls, the API call format for DreamFactory is:

http[s]:///api/v2/[ ]/[ ][?=]

The instance url is necessary in every API call. We can easily prepend it before every outgoing APIcall. For more detailed information on interceptors, check out the AngularJS docs.

In short, interceptors are factories which return an object with intercepting methods. For example, if we want to prepend every outgoing API url with the instance url we first do this:

angular.module('your-app', [])
.constant('INSTANCE_URL', 'http://sample-instance.cloud.dreamfactory.com')
.constant('DSP_API_KEY', 'YOUR-API-KEY')

We can then use an interceptor factory, like this:

.factory('httpInterceptor', function (INSTANCE_URL) {
 return {
  request: function (config) {
   // Prepend instance url before every api call
   if (config.url.indexOf('/api/v2') > -1) {
   config.url = INSTANCE_URL + config.url;
   };
 return config;
  }
 }
})

Like 'request', you can also use other methods which execute every time you request or receive a response. For example, you can have a 'response' which comes with apiResponse as an argument. The ‘response’ executes right after any API receives a response. The same goes for requestError and responseError methods in an interceptor.

Add the interceptor factory to the list of interceptors in your Angular module config.

.config([
 '$httpProvider',
 function ($httpProvider) {
  $httpProvider.interceptors.push('httpInterceptor');
 }
])

And that’s it. Now every API call is configured to include the instance url in the beginning of the url. You can also perform some other generic operations like setting headers before every call or wrapping/unwrapping data.

The other important configuration is to send the API key with every API call. You can achieve this with a bit of code in the run block of the Angular module.

.run([
 '$http', 'DSP_API_KEY',
 function ($http, DSP_API_KEY) {
  $http.defaults.headers.common['X-Dreamfactory-API-Key'] = DSP_API_KEY;
  }
])

This will add a header for API key in every call.

Login and registration

The login module takes care of user registration, login, and session management. Since we’ve already configured our app to prepend instance_url we don’t have to include it anymore our API calls.

angular.module('login')
.service('Login', [
 '$http', '$q', '$rootScope',
 function ($http, $q, $rootScope) {
  var handleResult = function (result) {
  // set default header for every call
   $http.defaults.headers.common['X-DreamFactory-Session-Token'] = result.data.session_token;
   $rootScope.user = result.data
  };
  // login user
  this.login = function (creds) {
   var deferred = $q.defer();
   $http.post('/api/v2/user/session', creds).then(function (result) {
    handleResult(result);
    deferred.resolve(result.data);
   }, deferred.reject);
   return deferred.promise;
  };
  //register new user
  this.register = function () {
   var deferred = $q.defer();
    $http.post('/api/v2/user/register?login=true', options).then(function (result) {
    handleResult(result)
    deferred.resolve(result.data);
   }, deferred.reject);
   return deferred.promise;
   }
 }
])

As shown above, there’s a dedicated service for handling the login and register calls to DreamFactory. The session token obtained from the server is added to the default list of headers and sent with every API call. If you want to exclude the token in specific API calls, you can use an interceptor as shown earlier (you can delete some headers before making the API call based on conditions). Here we assume that every API call needs the session token header.

Now in your controllers, you can call these functions and provide them with any necessary parameters. These functions return promises, so you can subscribe to error and success callbacks and show necessary notifications.

Getting records

Getting records is no different than making login and register API calls. Using Angular’s $http service we can make a simple GET API call to fetch records. Since we have already configured session-token, api-key headers and instance_url, no more extra configuration is required. Here’s an example of fetching contacts:

.controller([
 '$scope', '$http',
 function () {
  $http.get('/api/v2/_table/contact', {
   include_count: true
  }).then(function (result) {
   // success
   $scope.list = result.data.resource;
   $scope.meta = result.data.meta;
  }, function () {
  // error
  });
 }
])

In the above call, we add an extra parameter include_count which is converted to a query parameter by the $http service. This tells the server to embed record count in the API response.

Similarly, you can also use the filter query parameter to get a filtered list of records.

$http.get('/api/v2/_table/contact', {
 filter: 'id=2'
}).then(function (result) {
 // success
 }, function (error) {
  // error
})

What’s Next?

We’ve covered the basics of the Angular sample app in this blog post. Check out the GitHub repo for more demo code showing database CRUD operations as well as file CRUD operations. Please let us know what you think in the blog comments or on the forum.

 

Build an application AngularJS DreamFactory 2.0