├── .gitignore
├── index.html
├── package.json
├── readme.md
├── index.js
└── app.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Falcor Demo
6 |
7 |
8 |
9 |
10 | Event Data
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "falcor-demo",
3 | "version": "1.0.0",
4 | "description": "Demo code for Auth0's Falcor article",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "falcor"
11 | ],
12 | "author": "Ryan Chenkie",
13 | "license": "MIT",
14 | "dependencies": {
15 | "express": "^4.13.3",
16 | "falcor": "^0.1.12",
17 | "falcor-express": "^0.1.2",
18 | "falcor-router": "^0.2.9",
19 | "lodash": "^3.10.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Falcor Demo
2 |
3 | This is the code for Auth0's [Getting Started With Falcor](https://auth0.com/blog/2015/08/28/getting-started-with-falcor/) blog post. In the tutorial, we cover how to:
4 |
5 | 1. Setup a Falcor model on the client and prime it with data
6 | 2. Use Falcor's JSON Graph to avoid having duplicate data
7 | 3. Setup a Falcor Router with Falcor Express
8 | 4. Do a basic search using a Falcor route
9 |
10 | ## Installation
11 |
12 | Clone the repo and then run:
13 |
14 | npm install
15 | node index.js
16 |
17 | Navigate to localhost:3000
18 |
19 | ## Important Snippets
20 |
21 | A model cache can be setup on the front end with some data by using a `new falcor.Model`
22 |
23 | ```js
24 | var model = new falcor.Model({
25 | cache: {
26 | events: [
27 | {
28 | name: "ng-conf",
29 | description: "The world's best Angular Conference",
30 | stuff: "oh hey stuff",
31 | location: { city: "Salt Lake City", state: "Utah" }
32 | },
33 | {
34 | name: "React Rally",
35 | description: "Conference focusing on Facebook's React",
36 | location: { city: "Salt Lake City", state: "Utah" }
37 | },
38 |
39 | ...
40 | ```
41 |
42 | We can then `get` some of the data by providing a set of JavaScript paths to the specific data we are looking for.
43 |
44 | ```js
45 | model
46 | // To get the values on the "location" object, we need to pass the paths for the
47 | // keys on that object
48 | .get(["events", {from: 0, to: 2}, ["name", "description", "location"],["city", "state"]])
49 | .then(function(response) {
50 | document.getElementById("event-data").innerHTML = JSON.stringify(response, null, 2);
51 | });
52 | ```
53 |
54 | We can then move the data over the server to be served with `falcor-express`. We need to have `express` serve a `model.json` endpoint and return a `new Router`.
55 |
56 | ```js
57 | // We setup a model.json endpoint and pass it a dataSourceRoute which
58 | // allows us to serve a router. Various route requests can be sent to the
59 | // router to request whatever data is required
60 | app.use('/model.json', falcorExpress.dataSourceRoute(function(req, res) {
61 | return new Router([
62 | {
63 | // Our route needs to match a pattern of integers that
64 | // are used as eventIds
65 | route: "events[{integers:eventIds}]['name', 'description', 'location']",
66 | get: function(pathSet) {
67 |
68 | var results = [];
69 |
70 | // Above we specified an eventIds identifier that is an
71 | // array of ids which we can loop over
72 | pathSet.eventIds.forEach(function(eventId) {
73 |
74 | // Next, an array of key names that map is held at pathSet[2]
75 | pathSet[2].map(function(key) {
76 |
77 | // We find the event the cooresponds to the current eventId
78 | var eventRecord = eventsData.events[eventId];
79 |
80 | // Finally we push a path/value object onto
81 | // the results array
82 | results.push({
83 | path: ['events', eventId, key],
84 | value: eventRecord[key]
85 | });
86 | });
87 | });
88 |
89 | return results;
90 |
91 | ...
92 | ```
93 |
94 | ## License
95 | MIT
96 |
97 | ## What is Auth0?
98 |
99 | Auth0 helps you to:
100 |
101 | * Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, amont others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider**.
102 | * Add authentication through more traditional **[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**.
103 | * Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with the same user.
104 | * Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and **flow the user identity** securely.
105 | * Analytics of how, when and where users are logging in.
106 | * Pull data from other sources and add it to the user profile, through [JavaScript rules](https://docs.auth0.com/rules).
107 |
108 | ## Create a Free Auth0 Account
109 |
110 | 1. Go to [Auth0](https://auth0.com) and click Sign Up.
111 | 2. Use Google, GitHub or Microsoft Account to login.
112 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var falcor = require('falcor');
2 | var falcorExpress = require('falcor-express');
3 | var Router = require('falcor-router');
4 |
5 | var express = require('express');
6 | var _ = require('lodash');
7 | var app = express();
8 |
9 | // Have Express request index.html
10 | app.use(express.static('.'));
11 |
12 | var $ref = falcor.Model.ref;
13 |
14 | // Same data that was used in the view for our
15 | // events, but this time on a simple object
16 | // and not a Falcor model.
17 | var eventsData = {
18 | locationsById: {
19 | 1: {
20 | city: "Salt Lake City",
21 | state: "Utah"
22 | },
23 | 2: {
24 | city: "Las Vegas",
25 | state: "Nevada"
26 | },
27 | 3: {
28 | city: "Minneapolis",
29 | state: "Minnesota"
30 | },
31 | 4: {
32 | city: "Walker Creek Ranch",
33 | state: "California"
34 | }
35 | },
36 | events: [
37 | {
38 | name: "ng-conf",
39 | description: "The world's best Angular Conference",
40 | location: $ref('locationsById[1]')
41 | },
42 | {
43 | name: "React Rally",
44 | description: "Conference focusing on Facebook's React",
45 | location: $ref('locationsById[1]')
46 | },
47 | {
48 | name: "ng-Vegas",
49 | description: "Two days jam-packed with Angular goodness with a focus on Angular 2",
50 | location: $ref('locationsById[2]')
51 | },
52 | {
53 | name: "Midwest JS",
54 | description: "Midwest JS is a premier technology conference focused on the JavaScript ecosystem.",
55 | location: $ref('locationsById[3]')
56 | },
57 | {
58 | name: "NodeConf",
59 | description: "NodeConf is the longest running community driven conference for the Node community.",
60 | location: $ref('locationsById[4]')
61 | }
62 | ]
63 | }
64 |
65 | // We setup a model.json endpoint and pass it a dataSourceRoute which
66 | // allows us to serve a router. Various route requests can be sent to the
67 | // router to request whatever data is required
68 | app.use('/model.json', falcorExpress.dataSourceRoute(function(req, res) {
69 | return new Router([
70 | {
71 | // Our route needs to match a pattern of integers that
72 | // are used as eventIds
73 | route: "events[{integers:eventIds}]['name', 'description', 'location']",
74 | get: function(pathSet) {
75 |
76 | var results = [];
77 |
78 | // Above we specified an eventIds identifier that is an
79 | // array of ids which we can loop over
80 | pathSet.eventIds.forEach(function(eventId) {
81 |
82 | // Next, an array of key names that map is held at pathSet[2]
83 | pathSet[2].map(function(key) {
84 |
85 | // We find the event the cooresponds to the current eventId
86 | var eventRecord = eventsData.events[eventId];
87 |
88 | // Finally we push a path/value object onto
89 | // the results array
90 | results.push({
91 | path: ['events', eventId, key],
92 | value: eventRecord[key]
93 | });
94 | });
95 | });
96 |
97 | return results;
98 | }
99 | },
100 | {
101 | // Our route needs to match a pattern of integers that
102 | // are used as locationId
103 | route: "locationsById[{integers:locationId}]['city', 'state']",
104 | get: function(pathSet) {
105 |
106 | var results = [];
107 |
108 | // Above we specified an locationId identifier that is an
109 | // array of ids which we can loop over
110 | pathSet.locationId.forEach(function(locationId) {
111 |
112 | // Next, an array of key names that map is held at pathSet[2]
113 | pathSet[2].map(function(key) {
114 |
115 | // We find the event the cooresponds to the current locationId
116 | var location = eventsData.locationsById[locationId];
117 |
118 | // Finally we push a path/value object onto
119 | // the results array
120 | results.push({
121 | path: ['locationsById', locationId, key],
122 | value: location[key]
123 | });
124 | });
125 | });
126 |
127 | return results;
128 | }
129 | },
130 | {
131 | // The search route will match keys that match the names
132 | // of our conferences
133 | route: "events.byName[{keys}]['description']",
134 | get: function(pathSet) {
135 |
136 | var results = [];
137 | // We want to loop over each of the conference names provided
138 | pathSet[2].forEach(function(name) {
139 | // We also want to loop over all the events on the data object
140 | // and check if conference name is there
141 | eventsData.events.forEach(function(event) {
142 | if(_.contains(event, name)) {
143 | results.push({
144 | path: ['events','byName', name, 'description'],
145 | value: event.description
146 | });
147 | }
148 | });
149 | });
150 | return results;
151 | }
152 | }
153 | ]);
154 | }));
155 |
156 | app.listen(3000);
157 | console.log("Listening on http://localhost:3000");
158 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | /* --------------------------------------
2 |
3 | Below are several examples of hooking
4 | up a Falcor model and getting data from
5 | it to be displayed on the screen.
6 |
7 | Step 1: A local model cache is setup
8 | with some data. Data is requested with
9 | various get requests to the model.
10 |
11 | Step 2: We change the model over to a
12 | JSON Graph with references so that data
13 | isn't duplicated.
14 |
15 | Step 3: We move the data over to the
16 | server to be served from a Falcor Router.
17 | This step is currently setup to run but
18 | you can comment it out and start from
19 | Step 1 by uncommenting the appropriate
20 | pieces.
21 |
22 | ----------------------------------------*/
23 |
24 | /* === Step 1 === */
25 |
26 | // We can prime the model cache with a new falcor.Model
27 | // var model = new falcor.Model({
28 | // cache: {
29 | // events: [
30 | // {
31 | // name: "ng-conf",
32 | // description: "The world's best Angular Conference",
33 | // stuff: "oh hey stuff",
34 | // location: { city: "Salt Lake City", state: "Utah" }
35 | // },
36 | // {
37 | // name: "React Rally",
38 | // description: "Conference focusing on Facebook's React",
39 | // location: { city: "Salt Lake City", state: "Utah" }
40 | // },
41 | // {
42 | // name: "ng-Vegas",
43 | // description: "Two days jam-packed with Angular goodness with a focus on Angular 2",
44 | // location: { city: "Las Vegas", state: "Nevada" }
45 | // },
46 | // {
47 | // name: "Midwest JS",
48 | // description: "Midwest JS is a premier technology conference focused on the JavaScript ecosystem.",
49 | // location: { city: "Minneapolis", state: "Minnesota" }
50 | // },
51 | // {
52 | // name: "NodeConf",
53 | // description: "NodeConf is the longest running community driven conference for the Node community.",
54 | // location: { city: "Walker Creek Ranch", state: "California" }
55 | // }
56 | // ]
57 | // }
58 | // });
59 |
60 | // model
61 | // We want the name and description values for the first three items
62 | // from the data model
63 | // .get(["events", {from: 0, to: 2}, ["name", "description"]])
64 | // To get the values on the "location" object, we need to pass the paths for the
65 | // keys on that object
66 | // .get(["events", {from: 0, to: 2}, ["name", "description", "location"],["city", "state"]])
67 | // .then(function(response) {
68 | // document.getElementById("event-data").innerHTML = JSON.stringify(response, null, 2);
69 | // });
70 |
71 | // model
72 | // // We set the value of the first occurrence of Utah to UT
73 | // .set(falcor.pathValue(["events", 0, "location", "state"], 'UT'))
74 | // .then(function(response) {
75 | // model
76 | // // What we find afterwards is that the value gets changed in one location, but not both.
77 | // .get(["events", {from: 0, to: 2}, ["name", "description", "location"],["city", "state"]])
78 | // .then(function(response) {
79 | // document.getElementById('event-data').innerHTML = JSON.stringify(response, null, 2);
80 | // });
81 | // });
82 |
83 |
84 | /* === Step 2 === */
85 |
86 | // We can use the shorthand for references with a variable
87 | // var $ref = falcor.Model.ref;
88 | //
89 | // var model = new falcor.Model({
90 | // cache: {
91 | // locationsById: {
92 | // 1: {
93 | // city: "Salt Lake City",
94 | // state: "Utah"
95 | // },
96 | // 2: {
97 | // city: "Las Vegas",
98 | // state: "Nevada"
99 | // },
100 | // 3: {
101 | // city: "Minneapolis",
102 | // state: "Minnesota"
103 | // },
104 | // 4: {
105 | // city: "Walker Creek Ranch",
106 | // state: "California"
107 | // }
108 | // },
109 | // events: [
110 | // {
111 | // name: "ng-conf",
112 | // description: "The world's best Angular Conference",
113 | // location: $ref('locationsById[1]')
114 | // },
115 | // {
116 | // name: "React Rally",
117 | // description: "Conference focusing on Facebook's React",
118 | // location: $ref('locationsById[1]')
119 | // },
120 | // {
121 | // name: "ng-Vegas",
122 | // description: "Two days jam-packed with Angular goodness with a focus on Angular 2",
123 | // location: $ref('locationsById[2]')
124 | // },
125 | // {
126 | // name: "Midwest JS",
127 | // description: "Midwest JS is a premier technology conference focused on the JavaScript ecosystem.",
128 | // location: $ref('locationsById[3]')
129 | // },
130 | // {
131 | // name: "NodeConf",
132 | // description: "NodeConf is the longest running community driven conference for the Node community.",
133 | // location: $ref('locationsById[4]')
134 | // }
135 | // ]
136 | // }
137 | // });
138 | //
139 |
140 | // model
141 | // // Now when we set Utah to UT for the first occurrence, it is changed everywhere else
142 | // .set(falcor.pathValue(["events", 0, "location", "state"], 'UT'))
143 | // .then(function(response) {
144 | // model
145 | // .get(["events", {from: 0, to: 2}, ["name", "description", "location"],["city", "state"]])
146 | // .then(function(response) {
147 | // document.getElementById('event-data').innerHTML = JSON.stringify(response, null, 2);
148 | // });
149 | // });
150 |
151 |
152 | /* === Step 3 === */
153 |
154 | // We can set the model to have a data source that is retrieved from the backend
155 | // over HTTP by setting the soure to be a falcor.HttpDataSource.
156 | var model = new falcor.Model({source: new falcor.HttpDataSource('/model.json')});
157 | // .get(["events", {from: 0, to: 2}, ["name", "description"]])
158 | // .get([
159 | // "events", {from: 0, to: 2}, ["name", "description"]
160 | // ],
161 | // [
162 | // 'events', {from: 0, to: 2}, 'location', ['city', 'state']
163 | // ])
164 | // model
165 | // .get(["events", {from: 0, to: 2}, ["name", "description", "location"],["city", "state"]])
166 | //
167 | // .then(function(response) {
168 | // document.getElementById('event-data').innerHTML = JSON.stringify(response, null, 2);
169 | // });
170 |
171 | // Search example - we pass "Midwest JS" which will be looked up
172 | // in the events data on the server and sent back if it exists
173 | model
174 | .get(["events", "byName", ["Midwest JS"], ['description']])
175 | .then(function(response) {
176 | document.getElementById('event-data').innerHTML = JSON.stringify(response, null, 2);
177 | }, function(err) {
178 | console.log(err);
179 | // console.log(err['0'].value.message);
180 | });
181 |
--------------------------------------------------------------------------------