As u already know the Local resolvers are deprecated so we can't use it as a perspective way to handling REST cache. What we should use instead of resolvers?
'field policies' are not good for that at all. Let's imagine... You have two different client queries: getBooks and getBook. Each query getting data from the rest API. Somehow we need to handle the situation when we already got the data from getBooks and runing another query getBook. getBook should not make a request because the data were already cached. We did that in resolvers before it was deprecated. We were just checking the cache and return the data if it already exists in the cache if not did a request. How we can handle this in current circumstances?
Sorry but it's a bit not what I meant. Here is a code example:
export const getBooks = gql`
query getBooks () {
getBooks ()
#rest(
type: "Book"
path: "books"
endpoint: "v1"
) {
id
title
author
}
}
`
export const getBook = gql`
query getBook ($id: Int!) {
getBook (id: $id)
#rest(
type: "Book"
path: "book/{args.id}"
endpoint: "v1"
) {
id
title
author
}
}
`
So we have two different queries. The goal is when we run both in turn the getBook should not make a REST request because we already have the same data in the cache since we get it from getBooks. Before resolvers were deprecated we handle it in resolvers. Like: if this ID is not exist in the cache just make a request if exist give me data from the cache. How we can do that now?
As u can see fetchPolicy it's completely different.
Local fields it's also not good because it's something about fields not about the whole entity.
Related
I have two http endpoints setup:
GET /users/{userId}
GET /users/{userId}/notes/{noteId}
The GET User returns a payload which includes a list of multiple noteIds, which can be used to make multiple requests to the GET Note endpoint.
I am trying to configure Appsync to be able to fetch all of this data in a single query, but I can't get the list to populate with objects.
Schema:
type Query {
getUser(userId: String!): User
getNote(userId: String!, noteId: String!): Note
}
type User {
userId: ID!
firstName: String!
lastName: String!
notes: [Note]
}
type Note {
noteId: ID!
noteText: String!
createdDatetime: Int!
}
I have a data source setup for each of the endpoints and I have a resolver for getUser and for getNote - I also have a resolver for User.notes which is the same as getNote. These resolvers have this response mapping:
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
#if($ctx.result.statusCode == 200)
$ctx.result.body
#else
$utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
My resolver for the GET Note (including User.note field resolver) endpoint looks like this:
{
"version": "2018-05-29",
"method": "GET",
"resourcePath": $util.toJson("/prod/users/$ctx.args.userId/notes/$ctx.args.noteId"),
"params":{
"headers":{
"Content-Type": "application/json",
}
}
}
I can see from the logs, that Appsync attempts to run the GET Note resolver, but that the resource path doesn't seem to get populated with any ids? (I can see this in the custom Authorizer on the endpoint, which logs out the method ARN which still includes the $ctx.args...
It feels like this is a common use case, but I can't find a solution, or examples anywhere. Is my approach correct, or do I need a different solution?
I think the first problem is with your User.notes resolver and how you are accessing userId and noteId. When you have field resolvers, you should use ctx.source to access the the parent field [Ref.]. For example, you should use ctx.source.userId in your User.notes field resolver.
Secondly, as you are going to fetch individual notes from your getNote HTTP endpoint, AppSync supports this type of behavior when proxied through AWS Lambda using BatchInvoke. Please see "Advanced Use Case: Batching" on this link to get better idea. Also, I think this SO post is relevant to your use case.
One other possibility is to have another HTTP endpoint to get all the user's notes at once but I am not sure if this is possible in your case.
i'm new to all the hot graphql/apollo stuff.
I have a subscription which gets a search result:
export const SEARCH_RESULTS_SUBSCRIPTION = gql`
subscription onSearchResultsRetrieved($sid: String!) {
searchResultsRetrieved(sid: $sid) {
status
clusteredOffers {
id
}
}
}
`;
Is it possible to query the "status" field from client cache if i need it inside another component? Or do i have to use an additional ?
In the apollo dev-tools i can see that there is a cache entry under "ROOT_SUBSCRIPTION" not "ROOT_QUERY". What does that mean?
....thanks
I found out that subscribeToMore is my friend to solve this.
At first i wrote a normal query for the data i want to subscribe to have cached data, then the cache will be updated by the subscription.
<3 apollo
I have a query like this in my React/Apollo application:
const APPLICATIONS_QUERY = gql`
{
applications {
id
applicationType {
name
}
customer {
id
isActive
name
shortName
displayTimezone
}
deployments {
id
created
user {
id
username
}
}
baseUrl
customerIdentifier
hostInformation
kibanaUrl
sentryIssues
sentryShortName
serviceClass
updown
updownToken
}
}
`;
The majority of the items in the query are in a database and so the query is quick. But a couple of the items, like sentryIssues and updown rely on external API calls, so they make the duration of the query very long.
I'd like to split the query into the database portion and the external API portion so I can show the applications table immediately and add loading spinners for the two columns that hit an external API... But I can't find a good example of incremental/progressive querying or merging the results of two queries with Apollo.
This is a good example of where the #defer directive would be helpful. You can indicate which fields you want to defer for a given query like this:
const APPLICATIONS_QUERY = gql`
{
applications {
id
applicationType {
name
}
customer #defer {
id
isActive
name
shortName
displayTimezone
}
}
}
`
In this case, the client will make one request but receive 2 responses -- the initial response with all the requested fields sans customer and a second "patch" response with just the customer field that's fired once that resolver is finished. The client does the heavy lifting and pieces these two responses together for you -- there's no additional code necessary.
Please be aware that only nullable fields can be deferred, since the initial value sent with the first response will always be null. As a bonus, react-apollo exposes a loadingState property that you can use to check the loading state for your deferred fields:
<Query query={APPLICATIONS_QUERY}>
{({ loading, error, data, loadingState }) => {
const customerComponent = loadingState.applications.customer
? <CustomerInfo customer={data.applications.customer} />
: <LoadingIndicator />
// ...
}}
</Query>
The only downside is this is an experimental feature, so at the moment you have to install the alpha preview version of both apollo-server and the client libraries to use it.
See the docs for full details.
I have an Ember app with a login form which returns the current user in JSON format after successful login.
Using createRecord sets the returned JSON attributes directly on the model. For instance, is_private becomes user.is_private, not user.get('isPrivate')?
How do I load the user model so that the attributes are set correctly and I don't have to re-fetch it using the id?
As of a few days ago in ember data 1.0 beta you can use pushPayload to load data directly into the store. For example if you get data pushed to your app through WebSockets (we use the Heroku add-on Pusher). You can call it on the store (source) directly and it will pass it through the appropriate serializer:
var postsJSON = {
posts: [
{id: 1, post_title: "Great post"}
]
}
this.store.pushPayload('post',postsJSON)
NOTE that it will not currently load a singular object (ie post: {id: 1, post_title:"First!"}) - you need to format it as plural with an array.
DS.RESTSerializer has pushPayload as well (source), in which case you need to pass it the store instead.
I highly encourage reading the source code before using, as it looks like the implementation of it will be revisited.
Supposedly, the official way to do this is using adapter.load, as described in this thread:
Loading Data
Previously, some features of the store, such as load(), assumed a
single adapter.
If you want to load data from your backend without the application
asking for it (for example, through a WebSockets stream), use this
API:
store.adapterForType(App.Person).load(store, App.Person, payload);
This API will also handle sideloaded and embedded data. We plan to add
a more convenient version of this API in the future.
But unfortunately, it doesn't handle sideloaded data, despite what the documentation claims. I personally use something like the following, which is based on how find(ID) is implemented:
var id = json["person"]["id"];
var store = DS.get("defaultStore");
var adapter = store.adapterForType(App.Person);
adapter.didFindRecord(store, App.Person, json, id);
var person = App.Person.find(id);
Note that this code assumes JSON in the same format that find(ID) expects to receive from the server, as documented in the RESTAdapter guide:
{
person: {
id: 1,
is_private: false,
projects: [3]
},
projects: [
{ id: 3, name: "FooReader" }
]
}
This will apply any transformations you've configured using keyForAttributeName (such as mapping is_private to isPrivate), and it will handle sideloaded records. I'm not sure if this is a best practice, but it works quite well.
how about store.push('user', userJSON)?
http://emberjs.com/guides/models/pushing-records-into-the-store/#toc_pushing-records
All answers above did not work for me.
What only worked for me was:
this.store.buildRecord(this.store.modelFor('person'), data.id, data)
e.g.
FB.api('/me/permissions="user_photos"', function (response) { });
instead of
FB.api({ method: 'fql.query', query: 'SELECT user_photos FROM permissions WHERE uid=me()' }, function(resp) {
for(var key in resp[0]) {
if(resp[0][key] === "1")
console.log(key+' is granted')
else
console.log(key+' is not granted')
}
});
Yes, Theres a way called selection in graph api or more advance field expansion:
You can choose the fields (or connections) you want returned with the "fields" query parameter, Example:
FB.api('/me/permissions?fields=user_photos', function (response) { });
Graph API Explorer Demo
EDIT:
Quoted directly from graph api doc:
Selection
By default, most object properties are returned when you make a query.
You can choose the fields (or connections) you want returned with the
"fields" query parameter. For example, this URL will only return the
id, name, and picture of Ben:
https://graph.facebook.com/bgolub?fields=id,name,picture
You can also request multiple objects in a single query using the
"ids" query parameter. For example, the URL
https://graph.facebook.com?ids=arjun,vernal returns both profiles in
the same response.
The "ids" query parameter also accepts URLs. This is useful for
finding IDs of URLs in the Open Graph. For example:
https://graph.facebook.com/?ids=http://www.imdb.com/title/tt0117500/
Additionally, there is a special identifier me which refers to the
current user. So the URL https://graph.facebook.com/me returns the
active user's profile.
When retrieving Posts via the /home, /feed, or /posts connection, you
can restrict the results to only those with a location attached by
adding with=location to the URL parameters:
https://graph.facebook.com/me/home?with=location
Source: Graph API Docs