{"action":"created","issue":{"url":"https://api.github.com/repos/Netflix/falcor/issues/513","repository_url":"https://api.github.com/repos/Netflix/falcor","labels_url":"https://api.github.com/repos/Netflix/falcor/issues/513/labels{/name}","comments_url":"https://api.github.com/repos/Netflix/falcor/issues/513/comments","events_url":"https://api.github.com/repos/Netflix/falcor/issues/513/events","html_url":"https://github.com/Netflix/falcor/issues/513","id":104383950,"number":513,"title":"Example Falcor DataSource/Model to wrap existing RESTful resources","user":{"login":"baer","id":940667,"avatar_url":"https://avatars.githubusercontent.com/u/940667?v=3","gravatar_id":"","url":"https://api.github.com/users/baer","html_url":"https://github.com/baer","followers_url":"https://api.github.com/users/baer/followers","following_url":"https://api.github.com/users/baer/following{/other_user}","gists_url":"https://api.github.com/users/baer/gists{/gist_id}","starred_url":"https://api.github.com/users/baer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/baer/subscriptions","organizations_url":"https://api.github.com/users/baer/orgs","repos_url":"https://api.github.com/users/baer/repos","events_url":"https://api.github.com/users/baer/events{/privacy}","received_events_url":"https://api.github.com/users/baer/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":18,"created_at":"2015-09-02T01:04:35Z","updated_at":"2016-08-18T15:11:09Z","closed_at":"2016-02-05T16:10:32Z","body":"So I've now read all of the docs and watched the keynote + the 8 getting started videos and I'm still not that clear on how Falcor orchestrates data fetching. Furthermore since very few of us are lucky enough to work in greenfield projects I'd imagine a very common use case would be to start by wrapping an existing RESTful API. There are two specific scenariaos that I'm not sure about how to deal with:\r\n\r\n---\r\n\r\n* What would be the preferred way to expose the GET lists vs. GET details pattern. These two models effectively nested.\r\n\r\n`GET /users`\r\n```json\r\n[{\r\n  \"id\": \"123\",\r\n  \"givenName\": \"Brandon\",\r\n  \"surame\": \"Pricher\"\r\n}, {\r\n  ...\r\n}]\r\n```\r\n\r\n`GET /users/123`\r\n```json\r\n{\r\n    \"id\": \"123\",\r\n    \"givenName\": \"Brandon\",\r\n    \"surame\": \"Pricher\",\r\n    \"customerLifetimeValue\": \"909869.00\",\r\n    \"preferredLocale\": \"en-US\",\r\n}\r\n```\r\n\r\n---\r\n\r\n* To extend the above, lets say my view needs the Customer + the customer's address. Lets assume we have a user model but the address is an external lookup (USPS address validation for example). On the client sideI might deal with this as follows in the controller but the model seems to have some of this information already encoded in it. Furthermore, if a Falcor client doesn't request address, there is no reason to make that call. Does Falcor orchestrate dependent queries or is there a proper way to handle this?\r\n\r\n```js\r\ngetCustomerViewModel(id) {\r\n  let customer = getCustomer(id)\r\n  let address = customer.then(getCustomerAddress)\r\n\r\n  return Promise.all(customer, address)\r\n    .then((customer, address) => {\r\n      return customer.address = address;\r\n    }).catch(errorHandler)\r\n}\r\n```\r\n\r\nThis is what the view actually needs to render the page\r\n```json\r\n{\r\n    \"id\": \"123\",\r\n    \"givenName\": \"Brandon\",\r\n    \"surame\": \"Pricher\",\r\n    \"customerLifetimeValue\": \"909869.00\",\r\n    \"preferredLocale\": \"en-US\",\r\n    \"address\": {\r\n      \"street\": 123,\r\n      \"city\": \"Memphis\",\r\n      \"zip\": \"38120-0098\" \r\n    }\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/Netflix/falcor/issues/comments/240754324","html_url":"https://github.com/Netflix/falcor/issues/513#issuecomment-240754324","issue_url":"https://api.github.com/repos/Netflix/falcor/issues/513","id":240754324,"user":{"login":"gottfrois","id":943784,"avatar_url":"https://avatars.githubusercontent.com/u/943784?v=3","gravatar_id":"","url":"https://api.github.com/users/gottfrois","html_url":"https://github.com/gottfrois","followers_url":"https://api.github.com/users/gottfrois/followers","following_url":"https://api.github.com/users/gottfrois/following{/other_user}","gists_url":"https://api.github.com/users/gottfrois/gists{/gist_id}","starred_url":"https://api.github.com/users/gottfrois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gottfrois/subscriptions","organizations_url":"https://api.github.com/users/gottfrois/orgs","repos_url":"https://api.github.com/users/gottfrois/repos","events_url":"https://api.github.com/users/gottfrois/events{/privacy}","received_events_url":"https://api.github.com/users/gottfrois/received_events","type":"User","site_admin":false},"created_at":"2016-08-18T15:11:08Z","updated_at":"2016-08-18T15:11:08Z","body":"Thanks for the great posts here guys, really helped me to get started with my REST API integration. Specially @greim and @dallonf.\r\n\r\nLT; DR We can't cache request promises for the `customers.length` and `customers[{integers:indices}]` routes when dealing with paginated REST APIs.\r\n\r\nCorrect me if i'm wrong @dallonf but caching the request promises only work if the http query are identical. If you have paginated API endpoints you can't cache the promise between the `customers.length` and `customers[{integers:indices}]` route.\r\n\r\nFor example, let's say your `GET /customers` endpoint returns 50 results per page. Most of REST API would also return the total number of customers in the request header or some metadata node. An implementation of such paginated customer list would look like this:\r\n\r\n```js\r\n[\r\n  {\r\n    route: \"customers.length\",\r\n    get: async(function(pathSet) {\r\n      // we don't care how much customers we return here since we only care\r\n      // about the total number of customers given in the headers.\r\n      var response = await(request.get('/customers', { offset: 0, limit: 50 }));\r\n      var headers  = response.headers;\r\n\r\n      return {\r\n        path: ['customers', 'lenght'],\r\n        value: headers['x-total-customers']\r\n      };\r\n    })\r\n  },\r\n  {\r\n    route: \"customers[{integers:indices}]\",\r\n    get: async(function(pathSet) {\r\n      var offset    = pathSet.indices[0];\r\n      var limit     = pathSet.indices.length;\r\n      var response  = await(request.get('/customers', { offset: offset, limit: limit }));\r\n      var customers = response.data;\r\n\r\n      return pathSet.indices.map((idx) => {\r\n        return {\r\n          path: ['customers', idx],\r\n          value: { $type: 'ref', value: ['customersById', customers[idx].id] }\r\n        };\r\n      });\r\n    })\r\n  }\r\n]\r\n```\r\n\r\nNow the issue starts when we add promise caching and client request the following data:\r\n\r\n```js\r\nmodel.get(\r\n  ['customers', 'length'],\r\n  ['customers', { from: 50, to: 99 }]\r\n)\r\n```\r\n\r\n1. Falcor execute first the `customers.length` route\r\n2. We cache the http request promise server side\r\n3. Falcor execute the `customers[{integers:indices}]` route using the cached promise\r\n\r\nBut since the cached promise was `request.get('/customers', { offset: 0, limit: 50 })`, it will return segments 0 to 50 and not 50 to 99 like it should.\r\n\r\nMy conclusion is that we can only cache request promises if they share the same parameters and in the case of the `length` we can't avoid the extra http query.\r\n\r\nDo you guys have any idea how this could be avoided and still perform only 1 http query?"}}
{"actor":{"display_login":"gottfrois"}}