The document says the cache can be automatically updated in the following example:
{
post(id: '5') {
id
score
}
}
mutation {
upvotePost(id: '5') {
id
score
}
}
Would the automatic update work in the following case when the cached object is in a list ? Like this:
This is a query that fetches a list of reviews:
{
reviewList(model:12){
list{
reviewId
text
}
cursor
}
}
When I update one of the reviews from the list to the server, react apollo does not automatically update the cached review:
mutation{
updateReview(reviewId:'1',text:'new text'){
reviewId
text
}
}
Is it a must to use update properties of the mutation component to update the cache?
Assuming you're using apollo-cache-inmemory, unless you are adding an item to or removing an item from the list, it shouldn't be necessary to use update. However, it's important to keep in mind that the cached data is normalized, and that Apollo utilizes the __typename and id (or _id) field to generate the cache key for any queried object. From the docs:
The InMemoryCache normalizes your data before saving it to the store
by splitting the result into individual objects, creating a unique
identifier for each object, and storing those objects in a flattened
data structure. By default, InMemoryCache will attempt to use the
commonly found primary keys of id and _id for the unique identifier if
they exist along with __typename on an object.
If the id field is missing, Apollo won't know how to match up the data from your query and the data from your mutation:
If id and _id are not specified, or if __typename is not specified,
InMemoryCache will fall back to the path to the object in the query,
such as ROOT_QUERY.allPeople.0 for the first record returned on the
allPeople root query. That would make data for given type scoped for
allPeople query and other queries would have to fetch their own
separate objects.
Fortunately, there's a workaround. You can of course just rename reviewId to id on the server side. However, if you want the cache to just use the reviewId field, you can pass in a dataIdFromObject function to your InMemoryCache constructor:
import { InMemoryCache, defaultDataIdFromObject } from 'apollo-cache-inmemory'
const cache = new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
case 'Review': return object.reviewId
default: return defaultDataIdFromObject(object)
}
}
})
As long as resulting value is unique, you can utilize any other field (or combination of fields) in a similar fashion.
Related
I'm trying to create a blog page that can optionally display any image assets when they exist.
First, I created a content modal in Contentful with a Rich Text named "Body", and then I created an entry with some random text in it. In Gatsby, when I try to query the data via GraphQL explorer, I can query the "raw" field inside the "body". The "references" field can not be queried unless I add at least one image asset (aka, "dummy content").
below is my graphql query
export const query = graphql`
query ($slug: String!) {
contentfulBlog(slug: { eq: $slug }, node_locale: { eq: "en-US" }) {
title
body {
raw
references {
... on ContentfulAsset {
contentful_id
__typename
gatsbyImageData(formats: AUTO, layout: FULL_WIDTH)
}
}
}
}
}
`
The problem with the above query is that if there is no image asset found in a post, it will break the query. And my goal is to be able to optionally query the references field when there are assets available. But I'm not sure how to do it.
**Updated on Aug 24th, 2021:
For anyone that is also struggling with this problem as of today:
After digging through tons of documentation and answers, I've found out that this is in fact one of the limitations of the gatsby-source-contentful plugin. In short, the plugin(v4), as it states in its documentation:
At the moment, fields that do not have at least one populated instance
will not be created in the GraphQL schema.
Not sure if this will be addressed in a future release, but currently, it is required to have at least one dummy content(embedded image, links...etc) added in your rich text body in order for the plugin to create the "references" field in the graphql schema.
Here is the official discussion thread
If the idea is to only query references if they are available you need to customize the GraphQL schema to make the field nullable (meaning it can be empty, null) because, by default, it's inferred that it isn't.
You can check for Contentful content types in: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/content-types/content-model
This would be the ideal solution. Alternatively, according to this Medium article, you can bypass it by wrapping your offending node within a Node-type object. In your case:
export const query = graphql`
query ($slug: String!) {
contentfulBlog(slug: { eq: $slug }, node_locale: { eq: "en-US" }) {
title
body {
raw
references {
... on Node {
... on ContentfulAsset {
contentful_id
__typename
gatsbyImageData(formats: AUTO, layout: FULL_WIDTH)
}
}
}
}
}
}
`
You may need to tweak it. Without knowing your data and schema structure it's impossible to guess if the query above will work alone, but get the idea. Check it in the GrahiQL playground (localhost:8000/___graphql)
Other resources:
https://github.com/gatsbyjs/gatsby/issues/20696
https://github.com/gatsbyjs/gatsby/pull/20794
I get an error Cannot add property key, object is not extensible using "#apollo/client": "^3.0.2" with ant design.
I can successfully obtain data from the server with my graphql query, however, the problem is that, the data inside the return object is somehow unextendable. This never used to be a problem prior to using apollo client (using apollo boost).
In order to make my data work in a table in ant design, I have to pass in the array data obtained from the data object returned from the server. The part where I get the error is when I attempt to iterate through the items array, and add a key to each object, so that react won't complain in the console of a missing key element for the html.
const items = [...data.items];
console.log("items:", items);
// Add a key for each item to make React stop complaining
// Erorr occurs here!!!
items.forEach(item => {
item.key = item.id;
});
Is there a way we can remove this unnecessary functionality that's hindering our ability in modifying and extending results obtained from the server? Also I tried spreading the array making a deep copy, but that's not working either.
What do I need to do to make sure that I can add an additional key field in each array item, based on the id?
Addendum:
I don't actually need the code that does the items.forEach() stuff. However, if I don't add it, react will complain that the entries in the table are all missing a key value. This is my workaround since tables in ant design get their data from an array of objects, and thus, need to have a key value in there. Since querying data from the server typically doesn't include a key property in an object, this has to be manually added.
Managed to figure it out by deep copying the array data, via lodash.
Use _.cloneDeep() on the returned data's object that is the name of the object/table you are querying for.
import * as _ from "lodash";
import { Table } from "antd";
function Component({ id, query }) {
const { loading, error, data } = useQuery(query, {
variables: { id }
});
if (loading) return <div />;
if (error) return `Error! ${error}`;
const items = _.cloneDeep(data.items);
console.log("items:", items);
// Add a key for each item to make React stop complaining
items.forEach(item => {
item.key = item.id;
});
return (
<Table
dataSource={items}
/>
);
I have a search query which takes an variable and searches based on that.
On my home page I'd like to send this query 3 times with 3 different variables.
But if I do as above I can't get the results.
Here is the query:
const TOP_TEACHER_QUERY= gql`
query topTeachers {
searchBasicTeachersByRating(rating: 3) {
id
name
title
}
searchBasicTeachersByRating(rating: 4) {
id
name
title
isNew
}
}
}
and this is the function
allQueries() {
return this.apollo
.watchQuery<any>({
query: TOP_TEACHER_QUERY,
})
.valueChanges;
}
NOTE :
I have tried adding an interface and define the desired response data, but it has no effect
interface Response {
searchBasicTeachersByRatingMedium: Student[];
searchBasicTeachersByRatingHigh: Student[];
}
allQueries() {
return this.apollo
.watchQuery<any>({
query: TOP_TEACHER_QUERY,
})
.valueChanges;
}
THE DATA IS ONLY CONTAINING A LIST NAMED AFTER THE QUERY (searchBasicTeachersByRating)
I have tried the following query in graphql playground and it returns 2 arrays
but in Angular I can only get one
As a work around I created new queries at back-end, or sent 2 different queries.
But I want a solution for this approach.
Thanks in advance
When a selection set includes the same field multiple times, you need to utilize aliases for the duplicate fields:
searchBasicTeachersByRatingMedium: searchBasicTeachersByRating(rating: 3) {
id
name
title
}
searchBasicTeachersByRatingHigh: searchBasicTeachersByRating(rating: 4) {
id
name
title
isNew
}
After the request completes, each field will be available as a property on data under the provided alias. In the example above, we aliased both fields, but you could omit the alias from one of them.
My question is: i have a mutations config where i have a REQUIRE_CHILDREN config with children array of queries. How can i get all possible fields from a payload object?
{
type: 'REQUIRED_CHILDREN',
children: [
Relay.QL`
fragment on MyPayload {
me {
id
...others field
}
}`]
So how can i ask all possible fields from me object? If i point only fragment on MePayload { me } object relay still returns me me { id }. I want relay to return me all fields in me object. Thanks.
You can't - your client code needs to specify all the fields you want to fetch explicitly. Those fields are then statically validated by the babel-relay-plugin, etc.
You probably don't want to be using REQUIRED_CHILDREN either, by the way. That's only useful to fetch fields that are only accessible in the onSuccess callback of the mutation, and therefore are never written to the Relay store and accessible to Relay containers...
Normally creating Relay query object for queries with single definition can be done using:
const relayQuery = Relay.QL `
query UserRoute($id_0: ID!) {
user(id:$id_0) {
id,
name,
email
}
}
;
I have query string intercepted from the one that are sent over network. They normally have multiple definitions (eg. query, fragment, mutation, subscription). I want to create a Relay query object for that type of query. The following code throws error:
Relay transform error "You supplied a GraphQL document named 'network' with 2 definitions, but it must have exactly one definition." in file '/Users/ankitshah/lyearnapp/src/network.js'. Try updating your GraphQL schema if an argument/field/type was recently added.
for this code:
const relayQuery = Relay.QL `
query UserRoute($id_0: ID!) {
user(id:$id_0) {
id,
...F0
}
}
fragment F0 on User {
id,
name,
email
}
;
I want this because I want to update Relay Store using Relay.Store.getStoreData().updateQueryPayload(queryObject, payload) function. Any help is appreciated.
I hit the same error. You will need to compact your query to be without fragments like so:
const relayQuery = Relay.QL `
query UserRoute($id_0: ID!) {
user(id:$id_0) {
id,
name,
email
}
}
;
Then it worked.
Try Using namedspaces . Eg
user1 :user(id:$id_0) {
id,
name,
email
}
user2 :user(id:$id_1) {
id,
name,
email
}