├── .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 | 


--------------------------------------------------------------------------------