Relay-style pagination with Apollo Client - reactjs

I am trying to paginate 2 relay-style fields posts_connection and comments_connection
For instance, the query is extremely simple,
const QUERY = gql `
PostsQuery($comments_first: Int, commments_after: String) {
posts_connection {
edges {
node {
...INFO_ABOUT_A_POST
comments_connection(first:$comments_first, after:$commments_after) {
edges {
node {
...INFO_ABOUT_A_COMMENT
}
}
}
}
}
}
}
`
To do this with Apollo client, we configure the cache using relayStylePagination(), as in https://www.apollographql.com/docs/react/pagination/cursor-based/#relay-style-cursor-pagination
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
posts_connection: relayStylePagination(),
},
},
posts: { // posts is the type of a single post node
fields: {
keyFields: ["postid"],
comments_connection: relayStylePagination()
}
},
comments: {
keyFields: ["commentid"],
},
}
}
My process is
Run the initial query,
data has a single initial comment, COMMENT1
fetchMore comments
fetchMore({variables : {comments_after:PREVIOUS_END_CURSOR} })
We fetch a new comment COMMENT2
The issue: data should contain [ COMMENT1, COMMENT2]
Instead, the new comment overwrites the old one, and data only contains COMMENT2
Why is this happening and how do I solve it?

Related

Apollo client - add new item to list following mutation

I am working on a react app using "#apollo/client": "3.6.5". In the app a user can create payment requests that are displayed in a list.
I need to add a newly created payment request to the list of payment requests in the Apollo cache following the mutation to create one. The problem is the new payment request is not added to the cached list and Apollo does not output any error messages to explain why.
This is my version of the update function I've copied from the Apollo docs to try and update the cache.
const [onCreatePaymentRequest, createPaymentRequest] = useMutation<
CreatePaymentRequest,
CreatePaymentRequestVariables
>(CreatePaymentRequestMutation, {
update(cache, { data: { createPaymentRequest } }) {
cache.modify({
fields: {
paymentRequests(existingPaymentRequests = []) {
const newPaymentRequestRef = cache.writeFragment({
data: createPaymentRequest,
fragment: gql`
fragment NewPaymentRequest on PaymentRequest {
id
amount
status
}
`
});
return [...existingPaymentRequests, newPaymentRequestRef];
}
}
});
}
});
The mutation to create a new payment request:
export const CreatePaymentRequestMutation = gql`
mutation CreatePaymentRequest($input: CreatePaymentRequestInput!) {
createPaymentRequest(input: $input) {
paymentRequest {
id
amount
status
}
}
}
`;
This is query used to fetch payment requests:
export const GetAccountPaymentRequestsQuery = gql`
query GetAccountPaymentRequests(
$accountId: UUID!
$first: Int
$before: String
$last: Int
$after: String
) {
currentUser {
id
accountMembers(filter: { account: { id: $accountId } }) {
edges {
node {
account {
paymentRequests(
first: $first
last: $last
before: $before
after: $after
) {
edges {
node {
id
amount
status
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
}
}
}
}
`;
I think the problem may be that payment requests are nested within the schema. Using the useQuery hook I access the payment requests within a component using the following:
const paymentRequests = data.currentUser.accountMembers.edges[0].node.account.paymentRequests
.edges
I have tried numerous iterations of the update function to get this to work but so far no luck.
Thanks

React Apollo Client - query results mixing up in cache

I use apollo client to fetch book graphs and use relay style pagination. Both of the following NEW_BOOKS query and ALL_BOOKS query works fine independently.
Currently, I am using NEW_BOOKS in the home page and ALL_BOOKS in a popup in the home page.
When the Homepage is opened NEW_BOOKS gets loaded fine.
When the popup is opened and ALL_BOOKS is fetched, newBooks become undefined or the result of ALL_BOOKS query.
Why is this happening?
const { loading, data: newBooks, fetchMore, networkStatus } = useQuery(NEW_BOOKS, {
variables: {
first: PAGE_SIZE,
after: endCursor
},
notifyOnNetworkStatusChange: true
});
const NEW_BOOKS = gql`query GetNewBooks($first:Int!, $after:String){
books(
first: $first, after: $after,
filters: [
{
path: "isNew",
value: true
}
]
) {
totalCount
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
name
author {
id
name
}
}
}
}
}`;
-All books query filterable by name
const { loading, data: filteredBooks, fetchMore, networkStatus } = useQuery(ALL_BOOKS, {
variables: {
first: PAGE_SIZE,
after: endCursor,
name: nameFilter
},
notifyOnNetworkStatusChange: true
});
const ALL_BOOKS = gql`query GetAllBooks($first:Int!, $after:String, $name:String){
books(
first: $first, after: $after,
filters: [
{
path: "name",
value: $name,
type: "contains"
}
]
) {
totalCount
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
name
copiesSold
author {
id
name
}
}
}
}
}`;
The cache being used looks like this,
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
books: relayStylePagination(),
},
}
},
});
We have to pass keyArgs explictly when relayStylePagination or similar pagination is used.
A keyArgs: ["type"] field policy configuration means type is the only argument the cache should consider (in addition to the field name and the identity of the enclosing object) when accessing values for this field. A keyArgs: false configuration disables the whole system of differentiating field values by arguments, so the field's value will be identified only by the field's name (within some StoreObject), without any serialized arguments appended to it.
KeyArgs documentation here.
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
books: relayStylePagination(["name"]),
},
}
},
});

Passing Arguments to GraphQL with Next.JS and Apollo

A lot of this is very new to me since I have been a PHP developer for so long.
I am having a couple of issues that I know are easy to fix. I just have limited experience and am learning.
I cannot seem to pass the argument $token_id. I am using next JS, and I just want to take the number on the [id].js page, and pass it as the token ID.
I can't seem to get data from traitType, which is an object named "traits" which has an array of objects inside of it.
export async function getStaticProps(context) {
const client = new ApolloClient({
uri: 'http://localhost:3000/api/graphql',
cache: new InMemoryCache()
});
const token_id = String(context.params.id)
const { data } = await client.query({
query: gql`
query myQuery($token_id: String!) {
getData(token_id: $token_id) {
token_id
name
}
}
`
},{ variables: { token_id } });
props: {
data: data,
}
}
export async function getStaticPaths() {
return {
paths: [], //indicates that no page needs be created at build time
fallback: 'blocking' //indicates the type of fallback
}
}
My Type Definition
type Blah {
token_id: String
name: String
#traits: [traitType] #TODO: returns nothing since data is array
}
type traitType {
trait_type: String
value: String
}
I have to hard code the token
getData(token_id: "6") {
token_id
name
traits {
trait_type
}
}
I have tried several variations in the graphql query
query getDataQuery($token_id: String! = "6") {
getData(token_id: $token_id) {
Returns Null on traits, and $token id is null, and nothing is found
"traits": [
{
"trait_type": null,
"value": null
}
],
And my JSON from mongo db is an array with an object.
"traits" : [
{
"trait_type" : "birthday",
"value" : 1627423029.0,
}
]
I can post more than just fragments of code. Sorry for the formatting.

Apollo client specific updatequery for all items?

I have two models with the following relation:
Groups has many
Links
So basically my initial query is getAllGroups, which fetches all groups and its links.
I have a mutation which simply creates a link, what is the most efficient way to update my UI?
Currently I have:
export const withCreateLink = graphql(createLink, {
props({ownProps, mutate}) {
return {
createLink(url, description, group) {
return mutate({
variables: {
url,
description,
group
},
updateQueries: {
getAllGroups: (prev, {mutationResult}) => {
const newLink = mutationResult.data.createLink;
return update(prev, {
allGroups: {
links: {
$push: [newLink]
}
}
})
}
}
})
}
}
}
});
Basically I only want to fetch the group which has the link added to it, but I need to specify which group that is. How do I go about this?
I already figured it out, while creating other mutations this way i've found some different approaches to tackle this:
Option 1:
If you have access to the index of the current model that's in your cache, you can pass it as a prop and then use it inside of your query to target the one that it has been added to:
updateQueries: {
getAllGroups: (prev, {mutationResult}) => {
const newLink = mutationResult.data.createLink;
return update(prev, {
allGroups: {
[groupIndex]:
links: {
$push: [newLink]
}
}
})
}
}
Option 2: Using update instead of updateQueries, so you can read the result of the root query, map over it and then append the newly inserted data. After this you can write it to the cache.
update: (proxy, mutationResult) => {
const query = getAllGroups;
const data = proxy.readQuery({query});
data.allGroups.map((groupData) => {
if(groupData.id == group)
groupData.links.push(mutationResult.data.createLink);
});
proxy.writeQuery({
query,
data
})
}

Relay generating invalid query after using `setVariables`-- am I doing something wrong?

For some reason, if I generate a root query which takes in parameters before injecting the child component, like so:
import Relay from 'react-relay';
export default {
production: (Component) => Relay.QL`
query {
getProduction(id: $productionId) {
${Component.getFragment('production')}
}
}
`
};
Relay originally generates this query:
query MyProductionDetailsQuery($id_0:ID!,$where_1:ProductionRoleWhereArgs!) {
getProduction(id:$id_0) {
id,
...F0
}
}
fragment F0 on Production {
id,
...
_roles4oPiwv:roles(first:10,where:$where_1) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
}
}
variables:
{id_0: "UHJvZHVjdGlvbjoxNg==", where_1: {archived: {eq: true}}}
However, If the Component's relay container has variables of its own, running this.props.relay.setVariables({...variables}) completely changes the request query generated by relay into something like this:
query My_production_details_page_ProductionRelayQL($id_0:ID!,$where_1:ProductionRoleWhereArgs!) {
node(id:$id_0) {
...F0
}
}
fragment F0 on Production {
id,
_roles6J5gK:roles(first:10,where:$where_1) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
}
}
variables:
{id_0: "UHJvZHVjdGlvbjoxNg==", where_1: {archived: {eq: false}}}
However, setVariables works fine if I have a root query with no parameters:
import Relay from 'react-relay';
export default {
viewer: (Component, variables) => Relay.QL`
query {
viewer {
${Component.getFragment('viewer', { ...variables })}
}
}
`
};
Here's the generated query:
query ViewerQuery($where_0:ProductionWhereArgs!) {
viewer {
...F0
}
}
fragment F0 on Viewer {
user {
_productions2IPZAw:productions(first:10,where:$where_0) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}
}
variables:
{where_0: {expDate: {gt: "2016-11-04T16:29:11.677Z"}, archived: {eq: false}}}
After setVariables in the working setup:
query ViewerQuery($where_0:ProductionWhereArgs!) {
viewer {
...F0
}
}
fragment F0 on Viewer {
user {
_productions1CyNvL:productions(first:10,where:$where_0) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}
}
variables:
{where_0: {expDate: {lt: "2016-11-04T16:34:12.537Z"}, archived: {eq: false}}}
versions:
"react-relay": "^0.9.3",
"react-router-relay": "^0.13.5"
I'm not sure if I'm doing something wrong with the configuration, or if it's just a bug on Relay's end.
Does anyone know what might be causing this issue?
When you run setVariables it leads to refetching only necessary data.
Relay looks which part of query could be affected by changing variable and requests from GraphQL server needed fragment.
It is possible because of Node interface(i.e. fetching object by opaque Relay id). See more in documentation.
I think, in your case you should implement Node Interface for Production type on GraphQL server.

Resources