Apollo cache update is not reflected on paginated query - apollo-client

I have two components, one of the components creates new items, the other one displays them using an "infinite scroll list". These two components do not have a parent/child relationship and they're not rendered at the same time (they're on different "pages").
I've followed these docs and included the modified object in the mutation of my first component. And I can see the new object in the Apollo cache using dev tools. (Car:<some UUID> gets added in the cache after the mutation runs)
My paginated component is configured with relay style pagination, and the pagination works fine, but when I add a new item it doesn't appear in the list until I refresh the page.
My InMemoryCache looks like this:
...
typePolicies: {
// paginated results
Query: {
fields: {
cars: relayStylePagination()
}
},
CarsResult: {
fields: {
edges: {
// Concatenate the incoming list items with
// the existing list items.
merge(existing = [], incoming) {
return [...existing, ...incoming]
}
}
}
},
PageInfo: {
fields: {
endCursor: {
merge(existing, incoming) {
return incoming
}
}
}
}
}
The mutation looks like this:
${CAR_SUMMARY_FRAGMENT}
mutation CreateCar($name: String!) {
createCar(
input: {
name: $name
}
) {
...CarSummary
}
}
The CreateCar return type is Car
Then my paginated query:
query CarsPaginated($after: Cursor) {
cars(
page: { first: 25, after: $after }
orderBy: { field: CREATE_TIME, direction: DESC }
) {
edges {
node {
...CarSummary
}
}
totalCount
pageInfo {
hasNextPage
endCursor
}
}
}
The CarsPaginated return type is CarsResult:
type CarsResult {
edges: [CarEdge]
pageInfo: PageInfo!
totalCount: Int!
}
type CarEdge {
node: Car
cursor: Cursor!
}
Ideally, I'd like the new item to show up at the top of my items list on the other component.
I've tried to use the "refetchQueries" attribute but the paginated query is not active since the list component is not rendered at that time.
Maybe there's something I need to do in the typePolicies?

Related

How to cast into a type in GraphQL

For example, through GitHub explorer one can retrieve different types of time line items for a pull request (in this example PULL_REQUEST_COMMIT and PULL_REQUEST_REVIEW):
{
repository(name: "react", owner: "facebook") {
pullRequests(last: 10) {
nodes {
number
timelineItems(last: 10, itemTypes: [PULL_REQUEST_COMMIT, PULL_REQUEST_REVIEW]) {
nodes {
__typename
}
}
}
}
}
}
How can I now access different fields of the types PullRequestEvent or PullRequestReviewEvent? In other words, is there a cast or an if-then-else in GraphQL?
nodes returns an array of PullRequestTimelineItems and a PullRequestTimelineItemsis a GraphQL union type. You can use the ...on notation to query for fields of a specific member in the union type:
{
repository(name: "react", owner: "facebook") {
pullRequests(last: 10) {
nodes {
number
timelineItems(last: 10, itemTypes: [PULL_REQUEST_COMMIT, PULL_REQUEST_REVIEW]) {
nodes {
...on PullRequestReview {
body
}
...on PullRequestCommit {
url
}
}
}
}
}
}
}

apollo client cache for nested queries

I have a nested query (query inside query) with apollo client.
Everything works great, I do the request and get the correct data, but the issue is when I'm trying to use the cache, the cache returns undefined for the nested query prop.
My query:
query GetStudents($first: Int!, $after: String) {
me {
id
email
firstName
lastName
students(first: $first, after: $after) {
edges {
node {
id
created
number
status
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
When I try to use the inMemoryCache, the students is always undefined :
new InMemoryCache({
typePolicies: {
Query: {
fields: {
me: {
keyArgs: false,
merge(existing = {}, incoming = {}, { readField }) {
const id = readField("id", incoming);
const email = readField("email", incoming);
const students = readField("students", incoming);
return {
...
};
},
},
}
}
}
});
I can read correctly the id and email from the cache, but the students (which is the nested query) will be always undefined.
Do I need to read the cache students in a different way because it is a query?

Attempting to query with graphql where id is

I need to get a query using graphql in strapi/gatsby where id is {id}.
According to the documentation found here you query all like so:
{
allStrapiArticle {
edges {
node {
id
title
content
}
}
}
}
This works and I'm able to query however I'd like to get only one Article where id is {id};
I have tried:
{
allStrapiArticle(id: "4") {
edges {
node {
id
title
content
}
}
}
}
And also:
{
allStrapiArticle {
edges {
node(id: "4") {
id
title
content
}
}
}
}
Both of the above give me an error. Any idea how I can achieve this?
Use:
{
allStrapiArticle(filter: {id: {eq: "4" }}) {
edges {
node {
id
title
content
}
}
}
}
elemMatch filter might be useful for your use case as well.
Check the localhost:8000/___graphql playground to test your queries and filters.
More references:
https://www.gatsbyjs.com/docs/query-filters/
https://www.gatsbyjs.com/docs/graphql-reference/

Query multiple collections from Shopify in GatsbyJS

I want to display groups of Shopify products based on what collections they're associated with, using gatsby-source-shopify
Filtering to get all products from one collection as easy as running this query:
const { allShopifyCollection } = useStaticQuery(
graphql`
query {
allShopifyCollection(filter: {id: {in: "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzE3NzAxMjY3MDQ5OA=="}}) {
edges {
node {
products {
title
}
}
}
}
`
)
However it's not possible (to my knowledge) to query multiple times on the same data type in the same component.
What's the preferred way to approach this issue?
Use multiple components that fetches the data for each collection and
pass it to a grid component?
Fetch all collections and filter out each collection?
Another solution?
Can you use query aliases?
const { allShopifyCollection } = useStaticQuery(
graphql`
query {
collection1: allShopifyCollection(filter: {id: {in: "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzE3NzAxMjY3MDQ5OA=="}}) {
edges {
node {
products {
title
}
}
}
}
collection2: allShopifyCollection(filter: {id: {in: "Shopify__Collection__someOtherCollection"}}) {
edges {
node {
products {
title
}
}
}
}
collection3: allShopifyCollection(filter: {id: {in: "Shopify__Collection__yetAnotherCollection"}}) {
edges {
node {
products {
title
}
}
}
}
`
)

Optimistic update for a deletion mutation

I'm writing a deletion mutation. The mutation should delete a Key node and update the viewer's keys collection (I'm using Relay-style collections: viewer { keys(first: 3) { edges { node { ... }}}}.
Following the advice here, I'm using the FIELDS_CHANGE config for simplicity, and it's actually working:
export class DeleteKeyMutation extends Relay.Mutation {
static fragments = {
viewer: () => Relay.QL`
fragment on Viewer { id }
`,
};
getMutation() { return Relay.QL`mutation {deleteKey}`; }
getVariables() {
return {
id: this.props.id,
};
}
getFatQuery() {
return Relay.QL`
fragment on DeleteKeyPayload {
viewer { keys }
}
`;
}
getConfigs() {
return [
{
type: 'FIELDS_CHANGE',
fieldIDs: {
viewer: this.props.viewer.id,
},
},
];
}
}
Now, how should I write an optimistic mutation for this? I've tried different approaches but none worked.
Optimistic update in Relay is just a simulation of what the server will return if operation succeeds. In your case you are removing one key, meaning the result would be an object without that key.
getOptimisticUpdate() {
return {
viewer: {
id: this.props.viewer.id,
keys: {
edges: this.props.viewer.keys.edges.filter((keyEdge) => key.node.id !== this.props.id)
}
}
};
}
You will also need to include the keys to your fragments so they are available in the mutation.
static fragments = {
viewer: () => Relay.QL`
fragment on Viewer { id, keys { edges(first: 3) { node { id } }}
`,
};
The problem with this approach is that it relies on your mutation to know what's your current keys pagination. If you are operating on the whole Connection at once, it is fine, but if you are using Relay pagination you should consider using other mutation operations.
There is NODE_DELETE, which can delete all occurrences of your key from Relay store or you can use RANGE_DELETE to only delete it from your current connection.

Resources