Define graphQLSchema properly in Node.js - facebook-graph-api

Doing graphQL first time.I searched for resources but could not found a helpful one.
I have written the following schema, got some help from another stackoverflow post.
schema.js
function getDataFromUrl(){
return [
{
"EventCode": "ET00029280",
"EventType": "CT",
"EventTitle": "OYSTERS Beach Park",
"VenueName": "Newexcelsior",
"VenueRegion": "Mumbai"
},
{
"EventCode": "ET00030629",
"EventType": "CT",
"EventTitle": "Stand-Up Comedy: The Trial Room",
"VenueName": "Newexcelsior",
"VenueRegion": "Mumbai"
}
];
}
const eventType = new GraphQLObjectType({
name: 'Event',
fields: {
EventTitle: {
type: GraphQLString,
description: 'Event Title'
},
},
});
const eventListType = new GraphQLObjectType({
name: 'EventList',
fields: {
events: {
type: new GraphQLList(eventType),
description: 'List of items',
},
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
eventList: {
type: new GraphQLList(eventListType),
resolve: () => getDataFromUrl(),
}
}
})
});
module.exports = schema;
When I query
{
eventList {
events {
EventTitle
}
}
}
I get this response:
{
"data": {
"eventList": [
{
"events": null
},
{
"events": null
}
]
}
}
I am expecting some changes in my schema, however my desired response is
{
"data": [
{
"EventTitle": "OYSTERS Beach Park"
},
{
"EventTitle": "Stand-Up Comedy: The Trial Room"
}
]
}
Please also suggest some links where I learn basics.

It looks like what's tripping you up the most right now is how you're defining a list. There's no need to define a separate type called EventList -- when you specify GraphQLList(someOtherType) you are already telling GraphQL to expect an array of that particular type. Your current Schema is expecting an array of an array of types. Because the structure of the data you're passing in doesn't match your schema, GraphQL can't find a field called EventTitle to match against and so it's returning null.
The fix in this case is to just get rid of eventListType altogether and change the type of your eventList field to eventType instead.
The docs for GraphQL.js are probably going to be your best bet as far as learning the basics. The only problem is the examples they include are way too basic. Here is a list of projects on GitHub that you can checkout to see GraphQL in action.
If you are starting out, I would also highly recommend using Apollo's graphql-tools. Even if you don't use Apollo on the client-side, graphql-tools makes it ridiculously easy to set up the server. Your schema would be much more readable, since you would write it as string rather than an object like you do in vanilla GraphQL.js. And you can easily set up a GraphiQL endpoint in addition to your GraphQL one, which makes debugging much easier :)

Related

Ember model relationship not matching API

I have two models who look like this:
// models/database.js
export default DS.Model.extend({
title: DS.attr(),
/*...*/
documents: DS.hasMany('document')
});
// models/document.js
export default DS.model.extend({
name: DS.attr(),
/*...*/
database: DS.belongsTo('database')
});
Is also have some routes
Router.map(function() {
this.route('databases', function() {
this.route('database', { path: ":database_id" }, function() {
this.route('documents');
});
});
});
When I call /database/[ID]/documents if got nothing.
I think the problem is I'm working with an JSON HAL API and when I ask for databases on API I receive this kind of response.
{
"_embedded": {
"databases": [
{
"_links": {
"self": {
"href": "<API_URL>/databases/7ec39267b544424c/",
"reference": "7ec39267b544424c",
"title": "00000006"
}
},
"database": {
"name": "00000006",
"reference": "7ec39267b544424c",
"title": "****"
}
},
{
"_links": {
"self": {
"href": "<API_URL>/databases/aace19af004144a4/",
"reference": "aace19af004144a4",
"title": "00000007"
}
},
"database": {
"name": "00000007",
"reference": "aace19af004144a4",
"title": "******"
}
}
}]
}
As you can see there no documents attributes on JSON returned.
If I want a database's documents I've got to call [API_URL]/databases/[ID]/domains/[ID]/documents
There's still a way to link documents to a database by specifying that the documents are recoverable at this URL (/databases/[ID]/domains/[ID]/Documents)?
I found the solution.
I've override extractRelationships method in hal serializer like this:
extractRelationships(modelClass, resourceHash, included) {
resourceHash._links["documents"] = {
"href" : "/databases/" + resourceHash.id + "/domains/" + (resourceHash.name || resourceHash[modelClass.modelName].name) + "/documents/"
};
return this._super(modelClass, resourceHash, included);
}
I just add a link to documents for each databases returned by the API.
I've modified my route for documents like this
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model() {
return this.modelFor('databases.database').get('documents');
}
});
And now it works perfectly.
I think what you're looking for is a HAL API to JSON API converter. To avoid doing this yourself manually I'd recommend the http://github.com/201-created/ember-data-hal-9000 Ember plugin, which I've used myself to get Ember Data to play nice with Spring Boot.
If you wanted to see a simple example here's my client-side and server-side demo app.
Additionally, if you were curious what Ember, and JSON API don't use HAL you can read here JSON API - FAQ on HAL.

What Type of Firebase Data Structure Is Better?

I'm seeing mixed tutorials in Firebase that recommends to structure data like this:
posts: {
post_1: {
title: 'Some Title',
comments: {
comment_1: true,
comment_2: true,
...
comment_x: true
}
}
}
comments: {
comment_1: {
name: 'Foo'
},
comment_2: {
name: 'Bar'
},
...
comment_x: {
name: 'X'
}
}
and
posts: {
post_1: {
title: 'Some Title',
}
}
comments: {
post_1: {
comment_1: {
name: 'Foo'
},
comment_2: {
name: 'Bar'
},
...
comment_x: {
name: 'X'
}
}
}
I think the latter is better in terms of speed when querying, bulk writing, and security flexibility. Especially that when you have the 1st data structure and you query over the blogs just to find out it's title. It's gonna load tons of data if you have a million comments even if the value is just true (unless I'm missing something here).
My question is, for heavy data like those in social networks, is the 2nd data structure really that better compared to the 1st one? I'm not even convinced that the 1st one is better at any area than the 2nd one at all.
I'm torn because some Firebase tutorial uses the 1st data structure and I'm using the Emberfire web library from Firebase which enforces it if you want to completely embrace the library.
The second example is shallow(er) which is good for Firebase. The data is separated, again a good thing. The only downside (which really isn't a downside) is if you want to hit the database only once for the post and associated comments, instead of twice as in the second example; once for the post and again for the comments. Obviously #1 wins there but other than that #2 is the way to go.

How to Model.fetch(<object>) when the returned data is a single object

I want to make an API call for searching that looks like this:
https://myapi.com/search/<query>/<token>
where query is the search term and token (optional) is an alphanumeric set of characters which identifies the position of my latest batch of results, which is used for infinite scrolling.
This call returns the following JSON response:
{
"meta": { ... },
"results" {
"token": "125fwegg3t32",
"content": [
{
"id": "125125122778",
"text": "Lorem ipsum...",
...
},
{
"id": "125125122778",
"text": "Dolor sit amet...",
...
},
...
]
}
}
content is an array of (embedded) items that I'm displaying as search results. My models look like this:
App.Content = Em.Model.extend({
id: Em.attr(),
text: Em.attr(),
...
});
App.Results = Em.Model.extend({
token: Em.attr(),
content: Em.hasMany('App.Content', {
key: 'content',
embedded: true
})
});
In order to make that API call, I figured I have to do something like this:
App.Results.reopenClass({
adapter: Em.RESTAdapter.create({
findQuery: function(klass, records, params) {
var self = this,
url = this.buildURL(klass) + '/' + params.query;
if (params.token) {
url += '/' + params.token;
}
return this.ajax(url).then(function(data) {
self.didFindQuery(klass, records, params, data);
return records;
});
}
}),
url: 'https://myapi.com/search',
});
then somewhere in my routes do this:
App.Results.fetch({query: 'query', token: '12kgkj398512j'}).then(function(data) {
// do something
return data;
})
but because the API returns a single object and Em.RESTAdapter.findQuery expects an array, an error occurs when Ember Model tries to materialize the data. So how do I do this properly? I'm using the latest build of Ember Model.
By the way, I'm aware that it would be much more convenient if the API was designed in a way so I can just call App.Content.fetch(<object>), which would return a similar JSON response, but I would then be able to set the collectionKey option to content and my data would be properly materialized.
You simply need to override your models load() method to adjust the payload hash to what Ember.Model wants. There are no serializers in Ember.Model. There is both a class level load for handling collections and an instance level load for loading the JSON specific to a single model. You want to override the instance level load method to wrap the content key value in an array if its not one already.
I have been using Ember.Mode quite heavily and enhanced it for a number of my use cases and submitted PR's for both fixes and enhancements. Those PRs have been sitting there for a while with no response from the maintainers. I have now moved to Ember.Data which has been 'rebooted' so to speak and having a lot better result with it now.
I would strongly suggest walking away from Ember.Model as it appears dead with the new pragmatic direction Ember Data has taken and because the project maintainer doesn't appear to have any interest in it anymore.

Ember Data serialize relationship without parent ID

I'm building an adapter to wrap the Keen.io API, so far I've been able to successfully load the projects resource, however the returned object looks like this:
{
partners_url: "/3.0/projects/<ID>/partners",
name: "Project Name",
url: "/3.0/projects/<ID>",
saved_queries: [ ],
events_url: "/3.0/projects/<ID>/events",
id: "<ID>",
organization_id: "<ORG ID>",
queries_url: "/3.0/projects/<ID>/queries",
api_key: "<API KEY>",
events: [
{
url: "/3.0/projects/<ID>/events/user_signup",
name: "user_signup"
},
{
url: "/3.0/projects/<ID>/events/user_converted",
name: "user_converted"
},
{
url: "/3.0/projects/<ID>/events/user_created_project",
name: "user_created_project"
}
]
}
Excluding a lot of cruft, Ember has no trouble mapping the name and id attributes using the RESTSerializer, but if I add an events relation to my Project model it blows up with:
Error while loading route: TypeError: Cannot set property 'store' of undefined
at Ember.Object.extend.modelFor (http://localhost:3000/assets/ember-data.js?body=1:9813:23)
at Ember.Object.extend.recordForId (http://localhost:3000/assets/ember-data.js?body=1:9266:21)
at deserializeRecordId (http://localhost:3000/assets/ember-data.js?body=1:10197:27)
at deserializeRecordIds (http://localhost:3000/assets/ember-data.js?body=1:10211:9)
at http://localhost:3000/assets/ember-data.js?body=1:10177:11
at http://localhost:3000/assets/ember-data.js?body=1:8518:20
at http://localhost:3000/assets/ember.js?body=1:3404:16
at Object.OrderedSet.forEach (http://localhost:3000/assets/ember.js?body=1:3247:10)
at Object.Map.forEach (http://localhost:3000/assets/ember.js?body=1:3402:10)
at Function.Model.reopenClass.eachRelationship (http://localhost:3000/assets/ember-data.js?body=1:8517:42)
From my investigation this seems to be because it can't find the inverse relation to map an Event back to a Project because there's no parent ID.
Is it possible to create a relation in Ember Data to support this? Or is it possible to modify the Serializer to append a projectId to each event before loading?
I'm using Ember 1.5.0-beta.4 and Ember Data 1.0.0-beta.7+canary.f482da04.
Assuming your Project model is setup the following way:
App.Project = DS.Model.extend({
events: DS.hasMany('event');
});
You need to make sure that the JSON from your API is in a certain shape that Ember-Data expects.
{
"project": {
"id": 1,
"events": ["1", "2"],
},
"events": [{
"id": "1",
"name": "foo"
}, {
"id": "2",
"name": "bar"
}]
}
You can, however, implement extractArrayin your model's serializer to transform the JSON from the server into something similar like the above example.
There's a working example and an explanation in the Ember docs.

How to access complex nested json in ember.js

after thorough searching stackoverflow and reading through all of the documetation on emberjs.com I'm finding myself stuck. I have a complex json object that I'm trying to model and output in my ember project.
I don't have control over the JSON, otherwise I'd change it's format to be easier digested. That said, here is my problem.
I have the following json
[
{
"id":1,
"catId": "10051",
"catUrl": "path/to/location",
"childCount": "4",
"description": [{
"text": "Description Text"
}],
"identifier": "UNQ123456",
"partialResults": "false"
}
]
What I'm trying to get at is the text value in description. I've tried creating the hasMany and belongsTo nested model construct described on emberjs.com, as well as many other patterns that were described as answers here on stack overflow, yet none of them seem to work or match the data construct I have to work with.
I've even tried the anonymous function in the first block of code on this page. http://emberjs.com/guides/models/defining-models/ trying to traverse this to the text that I want.
Regardless, any help would be much appreciated.
You could define a custom data transform to handle your special JSON field. This can be done by using the DS.RESTAdapter.registerTransform function. Something like this should work for your use case:
DS.RESTAdapter.registerTransform('descriptionText', {
serialize: function(data) {
var text = data[0].text;
return text;
},
deserialize: function(text) {
var data = [Ember.create({text: text})];
return data;
}
});
And then use it as a custom attribute for your model:
App.MyModel = DS.Model.extend({
...
description: DS.attr('descriptionText')
});
Note that the name of the transform could be something else as descriptionText as long you use the same name for DS.attr(...).
Hope it helps.