How to build Easy Stats with LoopBack (Part 2)

Previously on How to build Easy Stats with LoopBack - Part 1 I explained how to install, configure and make basic use of the loopback-stats-mixin, but now I want to show a more advanced use of this mixin by getting statistical information from related models and by wrapping different datasets.

Project Description

In this tutorial we are going to continue working with our easy-stats-api ,

If you want to follow this series, you can fork the (Easy Stats Project) and I will be pushing branches with each step of it.

Articles Index

  • Part 1: Setting up the REST API.
  • Part 2: Creating Stats Endpoints.
  • Part 3: Setting up Angular 2 App.
  • Part 4: Build Chart Component
  • Bonus: Bulding a Real-Time Chart Application with [FireLoop]

Create a Model

In Part 1 we created an Order model from which we were able to get statistical information, but now we need to create a new model named Customer and set a 1 to many relationship between models. For this project we are going to build a model that will extend from PersistedModel.

1
2
3
4
5
6
7
$ slc loopback:model Customer
? Enter the model name: Customer
? Select the data-source to attach Customer to: db (memory)
? Select models base class PersistedModel
? Expose Customer via the REST API? Yes
? Custom plural form (used to build REST URL): customers
? Common model or server only? common

For now do not setup any property for Customer so hit return when properties are requested.

Update Order Model

But we do need to add an amount: number property to our Order Model so we are able make calculations other than by index.

1
2
3
4
5
$ slc loopback:property
? Select the model: Order
? Enter the property name: amount
? Property type: number
? Required? Yes

Setup Model Relationships

Customer HasMany Order.

1
2
3
4
5
6
7
$ slc loopback:relation
? Select the model to create the relationship from: Customer
? Relation type: has many
? Choose a model to create a relationship with: Order
? Enter the property name for the relation: orders
? Optionally enter a custom foreign key:
? Require a through model? No

And Order BelongsTo Customer

1
2
3
4
5
6
$ slc loopback:relation
? Select the model to create the relationship from: Order
? Relation type: belongs to
? Choose a model to create a relationship with: Customer
? Enter the property name for the relation: customer
? Optionally enter a custom foreign key:

Awesome, now we have a perfect usable API that can create customers and take orders from these customers, but we still need to configure the mixin for the newly created Customer model.

Configure Mixins

You will need to configure the loopback-ds-timestamp-mixin and loopback-stats-mixin within the customer.json file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"mixins": {
"Timestamp": true,
"Stats": [
{
"method": "customerOrderStats",
"endpoint": "/:id/orders/number-stats",
"type": "relation",
"relation": "orders",
"count": {
"on": "createdAt",
"by": "index"
}
},
{
"method": "customerIncomeStats",
"endpoint": "/:id/orders/income-stats",
"type": "relation",
"relation": "orders",
"count": {
"on": "createdAt",
"by": "amount"
}
}
]
}
}

The first method customerOrderStats will create an endpoint that will result in a dataset of How many orders have been placed by customer over time

The second method customerIncomeStats will create an endpoint that will result in a dataset of How much income we earned from by customer over time

Depending on which parameters you send to these endpoints you will be able to get datasets with ranges that goes from hourly, daily, weekly, monthly, yearly to a custom {start: Date, end: Date} Object.

Configure StatsWrapper Mixins

Ok so we are now making use of the loopback-stats-mixin in a more advanced, but the truth is that we still are just doing 2 endpoints, in one of the projects where I needed to implement this mixin I had to make almost 20 endpoints, so many of these would be used by a Mobile App, so what?

Well we want to be careful on number of requests we want to make to the server, so that is the reason I created the StatsWrapper mixin that is included within the loopback-stats-mixin, so we can group datasets.

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"mixins": {
"StatsWrapper": [
{
"method": "customerStatsWrapper",
"endpoint": "/:id/stats",
"type": "relation",
"relation": "orders",
"wraps": ["customerOrderStats", "customerIncomeStats"]
}
]
}
}

This will create a new endpoint that will fetch both datasets in just 1 request.

Now lets imagine that customerStatsWrapper represents a chart in our application section, but lets say we have multiple charts to serve, then we can build each of our datasets endpoints individually as we just did, then wrap or group these by charts, then you can also wrap the wrappers by creating a new wrapper that wraps above, crazy huh?, just think in a three of services that serves everything you need in 1 endpoint.

The described above will allow you to get a complex amount of datasets in 1 one HTTP Request, removing a huge amount of requests overhead.

Full Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
"mixins": {
"Timestamp": true,
"Stats": [
{
"method": "customerOrderStats",
"endpoint": "/:id/orders/number-stats",
"type": "relation",
"relation": "orders",
"count": {
"on": "createdAt",
"by": "index"
}
},
{
"method": "customerIncomeStats",
"endpoint": "/:id/orders/income-stats",
"type": "relation",
"relation": "orders",
"count": {
"on": "createdAt",
"by": "amount"
}
}
],
"StatsWrapper": [
{
"method": "customerStatsWrapper",
"endpoint": "/:id/stats",
"type": "relation",
"relation": "orders",
"wraps": ["customerOrderStats", "customerIncomeStats"]
}
]
}
}

So here we are, now we were able to build 2 statistical endpoints that fetches data related to specific customers, but also we creted a wrapper that allows to make just 1 call to get a grouped set of information.

Verify Endpoints

Now we can verify everything is working by running our API as follows:

1
2
3
$ slc run
Web server listening at: http://0.0.0.0:3000
Browse your REST API at http://0.0.0.0:3000/explorer

Now if we load the API Explorer into a browser by using the http://0.0.0.0:3000/explorer url we will be able to see a set of endpoints under the Customer category, open the [POST] /customers section and click the Try it out! button 1 time to create a new customer.

After you successfully create 1 customer, you will get a response with the number id 1.

Now start placing some orders for that customer within [POST] /customers/:id/orders, make sure you set different amounts.

Example: [POST] /customers/1/orders

1
2
3
{
"amount": 10
}

Example Result

1
2
3
4
5
6
7
{
"amount": 10,
"id": 1,
"createdAt": "2016-04-21T21:10:19.029Z",
"updatedAt": "2016-04-21T21:10:19.035Z",
"customerId": 1
}

Now we can get information of how many orders the customer have placed and the income from that customer

[GET] /customers/1/stats?range=daily

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
{
"customerOrderStats": [
{
"date": "2016-04-14T21:18:26.838Z",
"universal": 1460668706838,
"count": 0
},
{
"date": "2016-04-15T21:18:26.838Z",
"universal": 1460755106838,
"count": 0
},
{
"date": "2016-04-16T21:18:26.838Z",
"universal": 1460841506838,
"count": 0
},
{
"date": "2016-04-17T21:18:26.838Z",
"universal": 1460927906838,
"count": 0
},
{
"date": "2016-04-18T21:18:26.838Z",
"universal": 1461014306838,
"count": 0
},
{
"date": "2016-04-19T21:18:26.838Z",
"universal": 1461100706838,
"count": 0
},
{
"date": "2016-04-20T21:18:26.838Z",
"universal": 1461187106838,
"count": 0
},
{
"date": "2016-04-21T21:18:26.838Z",
"universal": 1461273506838,
"count": 1
}
],
"customerIncomeStats": [
{
"date": "2016-04-14T21:18:26.842Z",
"universal": 1460668706842,
"count": 0
},
{
"date": "2016-04-15T21:18:26.842Z",
"universal": 1460755106842,
"count": 0
},
{
"date": "2016-04-16T21:18:26.842Z",
"universal": 1460841506842,
"count": 0
},
{
"date": "2016-04-17T21:18:26.842Z",
"universal": 1460927906842,
"count": 0
},
{
"date": "2016-04-18T21:18:26.842Z",
"universal": 1461014306842,
"count": 0
},
{
"date": "2016-04-19T21:18:26.842Z",
"universal": 1461100706842,
"count": 0
},
{
"date": "2016-04-20T21:18:26.842Z",
"universal": 1461187106842,
"count": 0
},
{
"date": "2016-04-21T21:18:26.842Z",
"universal": 1461273506842,
"count": 10
}
]
}

As you can see we easily created 3 endpoints that can get different type of information but also we wrapped the results into 1 object, so improved developers experience and performance on requests.

What is next?

In my next blog I want to show you how to create some charts to represent this within an Angular 2 Application with some help of D3.

Furthermore, I will talk about even more advanced ways to use the loopback-stats-mixin.

If you like this series and want be aware of next releases and new packages, follow me on Twitter @johncasarrubias or leave a comment here.

Thanks for reading.


Comments:

...