The result of the mutation should update the data without an unnecessary request to the server, I do not understand what needs to be corrected so that the data is not fetched twice, but taken from the cache.
const [createEvent, { loading }] = useMutation(CREATE_GOOD_MUTATION, {
variables: values,
update(proxy, result) {
// TODO: remove goods from cache
const data = proxy.readQuery({
query: FETCH_ITEMS_QUERY,
});
let newData = [...data.events];
newData = [result.data.events, ...newData];
proxy.writeQuery({
query: FETCH_ITEMS_QUERY,
data: {
...data,
events: {
newData,
},
},
});
},
// refetchQueries: [
// {
// query: FETCH_ITEMS_QUERY,
// },
// ],
onError(err) {
setErrors(err.message);
},
});
enter image description here
Mutation to create a new good:
CREATE_GOOD_MUTATION = gql`
mutation createEvent(
$title: String!
$description: String!
$price: String!
$autor: String!
$pageNumber: String!
$publishYear: String!
) {
createEvent(
eventInput: {
title: $title
description: $description
price: $price
autor: $autor
pageNumber: $pageNumber
publishYear: $publishYear
}
) {
title
description
price
}
}
`;
Query to display all products:
FETCH_ITEMS_QUERY = gql`
query events {
events {
id
title
description
price
}
}
`;
I need to fetch it once, when I try to create a new good by mutation and display all products from the cache, without an additional request.
You should use proxy.modify instead of proxy.readQuery/proxy.writeQuery.
Using cache.modify
Making cache updates with mutation.update function
Try to change your mutation update function to something like this:
update(proxy, result) {
proxy.modify({
fields: {
events(existingEvents = []) {
return [...existingEvents, result.data.createEvent];
}
}
})
},
Related
I am using Apollo Server / Client and the cache does not seem to work on update mutations. (Create, Delete). The server gets updated but nothing happens on the front end. I have to reload the page to show the new item / show change of an item.
I followed the Apollo docs and modeled it after their sandbox implementation.
Let me know if you need more of my code, thank you.
Here is my code:
<form
onSubmit={(e) => {
e.preventDefault();
createUser(
{
variables: {
name: input.value,
email: input.value,
password: input.value
}
},
{
update(cache, { data: { createUser } }) {
cache.modify({
fields: {
allUsers(existingUsers = []) {
const newUser = cache.writeFragment({
data: { createUser },
fragment: gql`
fragment NewUser on User {
name
email
}
`
});
return existingUsers.concat(newUser);
}
}
});
}
}
);
}}
>
You need to provide an id property in the writeFragment method. Here's the example on the docs:
client.writeFragment({
id: '5',
fragment: gql`
fragment MyTodo on Todo {
completed
}
`,
data: {
completed: true,
},
});
Also, writeFragment returns void, so you need to use readFragment to get the data you want, or just use the data available in the mutation's result
Hy I'm using the Apollo Client with React. I query the posts with many different variables. So I have one post in different "caches". Now I want to delete a post. So I need to delete this specific post from all "caches".
const client = new ApolloClient({
link: errorLink.concat(authLink.concat(httpLink)),
cache: new InMemoryCache()
});
Postquery:
export const POSTS = gql`
query posts(
$after: String
$orderBy: PostOrderByInput
$tags: JSONObject
$search: String
$orderByTime: Int
) {
posts(
after: $after
orderBy: $orderBy
tags: $tags
search: $search
orderByTime: $orderByTime
) {
id
title
...
}
}
`;
I tried it with the cache.modify(), which is undefined in my mutation([https://www.apollographql.com/docs/react/caching/cache-interaction/#cachemodify][1])
const [deletePost] = useMutation(DELETE_POST, {
onError: (er) => {
console.log(er);
},
update(cache, data) {
console.log(cache.modify())//UNDEFINED!!!
cache.modify({
id: cache.identify(thread), //identify is UNDEFINED + what is thread
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
}
}
})
}
});
I also used the useApolloClient() with the same result.
THX for any help.
Instead of using cache.modify you can use cache.evict, which makes the code much shorter:
deletePost({
variables: { id },
update(cache) {
const normalizedId = cache.identify({ id, __typename: 'Post' });
cache.evict({ id: normalizedId });
cache.gc();
}
});
this option worked for me
const GET_TASKS = gql`
query tasks($listID: String!) {
tasks(listID: $listID) {
_id
title
sort
}
}
`;
const REMOVE_TASK = gql`
mutation removeTask($_id: String) {
removeTask(_id: $_id) {
_id
}
}
`;
const Tasks = () => {
const { loading, error, data } = useQuery(GET_TASKS, {
variables: { listID: '1' },
});
сonst [removeTask] = useMutation(REMOVE_TASK);
const handleRemoveItem = _id => {
removeTask({
variables: { _id },
update(cache) {
cache.modify({
fields: {
tasks(existingTaskRefs, { readField }) {
return existingTaskRefs.filter(
taskRef => _id !== readField('_id', taskRef),
);
},
},
});
},
});
};
return (...);
};
You can pass your updater to the useMutation or to the deletePost. It should be easier with deletePost since it probably knows what it tries to delete:
deletePost({
variables: { idToRemove },
update(cache) {
cache.modify({
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
},
},
});
},
});
You should change variables to match your mutation. This should work since posts is at top level of your query. With deeper fields you'll need a way to get the id of the parent object. readQuery or a chain of readField from the top might help you with that.
I am trying to update the object inside the document
Document: Cats
{
"_id": "5e5cb512e90bd40017385305",
"type": "cat"
"history": [
{
"id": "randomID",
"content": "xyz",
},
{
"id": "randomID2",
"content": "abc",
}
]
}
Code to select and update the object inside the history array:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true,
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Input has following values
input: {
catId: "5e5cb512e90bd40017385305",
historyId: "randomID",
history: {
id: "randomID",
content: "new content"
}}
I tried using Projection, I used select changed it to field, found in mongoose documentation.
I still couldn't update the values. Is there anything wrong with the way i am querying or selecting the subfield.
Found the Solution for it by going through more detail of the operator($set) and option(new, fields).
Question:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
// using new option would return the new document
new: true,
/* using fields option would select the based on the given key, but using new:
true with fields will throw error: 'cannot use a positional projection and
return the new document'
*/
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
This post below answers that question for *error: 'cannot use a positional projection and return the new document'.
https://stackoverflow.com/a/46064082/5492398
Final Solution:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Removing field option, since I don't need the unmodified selection before atomic modification, solves the question.
Doing nightlife app on freecodecamp https://learn.freecodecamp.org/coding-interview-prep/take-home-projects/build-a-nightlife-coordination-app/
I am trying to implement 'Go' button, similarly 'Like' button on Youtube or Instagram. Users click the button the number(counting how many users go) goes up meaning users will go there and click again, it revokes, the number decreases, users will not go there.
It seems like working well except the issue, I have to refresh the page and then, the number has increased or decreased and throws the error like below so:
Invariant Violation: Can't find field getBars({}) on object {
"getBars({\"location\":\"vancouver\"})": [
{
"type": "id",
"generated": false,
"id": "Bar:uNgTjA9ADe_6LWby20Af8g",
"typename": "Bar"
},
{
"type": "id",
"generated": false,
"id": "Bar:CwL5jwXhImT_7K5IB7mOvA",
"typename": "Bar"
},
{
"type": "id",
"generated": false,
"id": "Bar:mdt1tLbkZcOS2CsEbVF9Xg",
"typename": "Bar"
},
.
.
.
I am assuming handling update function will fix this issue but unlike the example from Apollo documentation:
// GET_TODOS is not dynamic query
// nothing to pass as variables to fetch TODO list
<Mutation
mutation={ADD_TODO}
update={(cache, { data: { addTodo } }) => {
const { todos } = cache.readQuery({ query: GET_TODOS });
cache.writeQuery({
query: GET_TODOS,
data: { todos: todos.concat([addTodo]) },
});
}}
>
My query is dynamic:
// I have to pass location variable, otherwise it won't fetch anything.
const GET_BARS_QUERY = gql`
query getBars($location: String!) {
getBars(location: $location) {
id
name
url
rating
price
image_url
goings {
username
}
goingCount
}
}
`;
I believe I might need to handle to provide location using readQuery and writeQury but not too sure what I should do.
Here's my code:
const GoButton = ({ user, bar }) => {
const { token } = user;
const { id, goings, goingCount } = bar;
const [userGoes] = useMutation(GO_MUTATION, {
variables: { yelp_id: id },
update(proxy, result) {
const data = proxy.readQuery({
query: GET_BARS_QUERY
});
data.getBars = [result.userGoes, ...data.getBars];
proxy.writeQuery({ query: GET_BARS_QUERY, data });
}
});
return (
<Button onClick={userGoes}>
Go {goingCount}
</Button>
);
};
const GO_MUTATION = gql`
mutation go($yelp_id: String!) {
go(yelp_id: $yelp_id) {
id
goings {
id
username
}
goingCount
}
}
`;
export default GoButton;
Full code here https://github.com/footlessbird/Nightlife-Coordination-App
when you read/write the getBars query, you need to pass the location as a variable
const [userGoes] = useMutation(GO_MUTATION, {
variables: { yelp_id: id },
update(proxy, result) {
const data = proxy.readQuery({
query: GET_BARS_QUERY,
variables: {
location: 'New York'
}
});
data.getBars = [result.userGoes, ...data.getBars];
proxy.writeQuery({ query: GET_BARS_QUERY, data,
variables: {
location: 'New York'
}
});
}
});
I am trying to use graphql for one of the GET request based on an id. Here's the code:
const { graphql, buildSchema } = require('graphql');
EmployeeService.prototype.getEmployee = function() {
// Construct a schema
const schema = buildSchema(`
type Query {
employee(id="12345") {
id
items {
id
name
}
}
}
`);
// The root provides a resolver function
let root = {
employee: () => id
};
// Run the GraphQL query
graphql(schema, '{ employee }', root).then((response) => {
console.log(response);
});
};
Trying to follow the documentation on http://graphql.org/graphql-js/.
I get a GraphQL error: "Syntax Error GraphQL request (3:19) Expected :, found =↵↵2: type Query {↵3: employee (id="12345") {↵ ^↵4: id↵"
Please advise.
You are maybe mixing things a little bit up. The schema and resolvers are part of your API and are not needed to make a query on a client. Just for demonstration purposes here a valid schema definition (that would normally run on a API server):
let schema = buildSchema(`
type Item {
id: Int!
name: String!
}
type Employee {
id: Int!
items: [Item]
}
type Query {
employee(id: Int!): Employee
}
`);
You then define your types and resolvers (simplified examples):
class Employee {
constructor(id, items) {
this.id = id;
this.items = items;
}
}
let root = {
employee: ({id}) => {
return new Employee(id, [{id: 1, name: 'Item 1'}, {id: 2, name: 'Item2'}]);
}
};
You can then run a query:
const query = `
{
employee(id: 1) {
id,
items {
id,
name
}
}
}
`;
graphql(schema, query, root).then((response) => {
console.log(response.data);
});
To run actual queries against a remote API have a look at GraphQL clients like Apollo or lokka