Apollo-client State management cache is not updating automatically after save and delete operation - apollo-client

I am using apollo-client for graphql calls along with I added state management with in the package apollo-client. In graphql module, assigned InMemoryCache to cache variable and client variable is exported.Client variable is imported in component so data is available in client.cache.data after default get call executed but I want to update client cache after save and delete graphql operations success callbacks
Here is my graphql.module.ts:
import {NgModule} from '#angular/core';
import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
import { ApolloClient } from 'apollo-client';
import {InMemoryCache} from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
const cache = new InMemoryCache();
const link = new HttpLink({
uri:'https://api.graph.cool/simple/v1/ciyz901en4j590185wkmexyex'
});
export var client = new ApolloClient({
cache,
link
});
#NgModule({
})
and my service call implementation
client
.query({
query: gql`
{
fORoomTypes()
{
nodes
{
roomTypeId
roomType
ratSingle
ratDouble
ratExtra
statu
}
}
}
`,
})
.then(result => {
callback(result);
});
after callback client.cache.data contain data, I want to call this data with cache queries and I want to update cache automatically
this is my save service implementation
const post = gql`
mutation
{
saveRtype(rt:
{
rty:
{
rt:"Club room"
rat:4000
cchub:4500
ext:800
statu:1
}
traCheck:1
}
)
}
`
client.mutate({
mutation: post
}).then((data) => {
callback(data)
});

I've only used apollo client with React but hopefully can shed light on how it works. For this to happen "automatically", you either need to call refetchQueries to refetch fORoomTypes after the mutation, or manually update the cache. I noticed your responses do not return an id property, so how would it know which one to update? If there is an identifier that isn't called "id", then register it following this documentation.
Here is the link to documentation in general for what you want
https://www.apollographql.com/docs/angular/features/cache-updates/

Related

How to get all cache data using reactjs #apollo/client v3

Is there anyway that I can check all the cache, eg: changes within apollo for debugging.
Something like redux store, where you can view the whole state tree.
They mentioned:
The cache stores the objects by ID in a flat lookup table.
https://www.apollographql.com/docs/react/caching/cache-configuration/
Any way to display/console the whole lookup table?
For #apollo/client v3
Found the answer, if anyone is interested.
Through InMemoryCache
You can console log the cache object where you create with InMemoryCache.
You should be able to find it under your created cache:
const cache = new InMemoryCache({"...Your option"})
console.log(cache.data) // <- Your cache query
Through browser console
Through browser, use console to log data
__APOLLO_CLIENT__.cache.data
Through apollo v3
Access through apollo client cache
const client = useApolloClient();
const serializedState = client.cache.extract();
console.log(serializedState) <- your cache query
I just installed Apollo Client extension for Chrome, seem to work, there is now "Apollo" tab in the dev tools
https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm?hl=en-US
I used "readQuery" method to get data from cache, might be not best way to do it.
import { useApolloClient, gql } from '#apollo/client';
...
const WEBSITE_TITLE = gql`
query GetSitewide {
sitewide {
data {
attributes {
header {
__typename
id
siteTitle
}
}
}
}
}
`;
...
function WebsiteTitle() {
const client = useApolloClient();
const {
sitewide: {
data: {
attributes: { header },
},
},
} = client.readQuery({
query: WEBSITE_TITLE,
});
const { siteTitle } = header;
return <> {siteTitle} </>;
}
export default WebsiteTitle;

Apollo on local client doesn't trigger the local resolvers

Apollo doesn't trigger the resolvers in the case of Local state Client (frontent local state). Apollo 2.7
Does anyone have any idea why it happens?
Here is the setup:
Apollo client
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-unfetch'
import { resolvers, typeDefs } from './resolvers';
import { initCache } from './init-cache';
export default function createApolloClient(initialState, ctx) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
return new ApolloClient({
ssrMode: Boolean(ctx),
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'include', // Additional fetch() options like `credentials` or `headers`
fetch,
}),
typeDefs,
resolvers,
connectToDevTools: true,
cache: initCache({
robot: {
__typename: 'Robot',
name: 'Robbie',
status: 'live',
},
member: {
__typename: 'Member',
name: 'RFesagfd',
}
}),
})
}
Types & resolvers (resolvers.js)
import gql from 'graphql-tag';
export const typeDefs = gql`
type Robot {
name: String!
status: String!
}
type Member {
name: String!
isLogged: Boolean!
}
`;
export const resolvers = {
Member: {
isLogged: (...args) => {
console.log('args', args); // THIS NEVER TRIGGERS SOMEHOW
return true;
}
}
};
Query
const GET_IS_MEMBER_LOGGED = gql`
query isMemberLogged {
member #client {
name
isLogged
}
}
`;
Thanks for any help!
You need to define result type of local queries:
const typeDefs = gql`
extend type Query {
robot: Robot
member: Member
}
... and resolver for your query - not type (as you decorated entire query as local)... but you have to return typed data:
export const resolvers = {
Query: {
member: (...args) => {
console.log('args', args);
return {
__typename: 'Member',
name: 'some name', // read from cache
isLogged: true // function result
};
}
}
};
You should also use __typename for cache writes.
update
assuming you have a Memeber in cache ... you can:
// read (initialized with permanent) data:
const memberData = cache.readQuery(....
// f.e. it should have `__typename` and 'name`
// ... and 'decorate' it with derived properites
memberData.age = currentYear - memberData.birthYear;
memberData.isLogged = someFuncReturningBool();
return memberData; // Member type shaped object
It's about shape/data organization - typed (return type shaped object with defined properties) or simple (return all properties separately) or mixed, f.e. (some global app state)
const GET_IS_MEMBER_LOGGED = gql`
query profileViewData {
member #client {
name
isLogged
}
isProfilePanelOpen #client
termsAccepted #client
}
`;
I found a possible solution. Maybe this info will be useful for someone.
If we want to omit the Query Resolver + Field resolvers and we want to have the only Field resolver we need to use #client(always: true).
The in deep explanation
In general, there is a problem with how the Apollo client works with Cache.
By default, it caches the response, and next time it'll fetch the cached result from the cache (eg. optimistic UI). This behavior is the same even in the case of the Client.
It means when we have the initial model in cache Apollo will fetch in from the cache and ignores the resolvers, even if we pass the #client directive.
To solve this problem and let Apollo know that we need to use Local resolvers EVEN if we have a cached object, we need to use #client(always: true) for the preferred field or the whole object. I made an example below.
P.S. Unfortunately I didn't find how to force Apollo to work with non-existing field so if we want to have some resolver for a specific field, we still need to define the initial field value it the initial Cached Model to let the Apollo know about this field. After that, Apollo will use resolver for it to generate some high-calculated output for this particular field, thanks to #client(always: true).
In general, it's ok, because we should know what kind of dynamic field we'll have in our model.
Apollo client
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-unfetch'
import { resolvers, typeDefs } from './resolvers';
import { initCache } from './init-cache';
export default function createApolloClient(initialState, ctx) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
return new ApolloClient({
ssrMode: Boolean(ctx),
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'include', // Additional fetch() options like `credentials` or `headers`
fetch,
}),
typeDefs,
resolvers,
connectToDevTools: true,
cache: initCache({
author: {
__typename: 'Author',
posts: 0,
name: '' // NEED TO SET AN INITIAL VALUE
}
})
}
Types & resolvers (resolvers.js)
import gql from 'graphql-tag';
import { print } from 'graphql';
export const typeDefs = gql`
type Author {
posts: Int!
name: String
}
`;
export const resolvers = {
Author: {
name(author) {
console.log('Author name resolver', author). // WORKS
return 'NAME';
},
},
};
Query
const GET_AUTHOR = gql`
query getAuthor {
author {
posts
name #client(always: true)
}
}
`;

Apollo useQuery() - "refetch" is ignored if the response is the same

I am trying to use Apollo-client to pull my users info and stuck with this problem:
I have this Container component responsible for pulling the user's data (not authentication) once it is rendered. User may be logged in or not, the query returns either viewer = null or viewer = {...usersProps}.
Container makes the request const { data, refetch } = useQuery<Viewer>(VIEWER);, successfully receives the response and saves it in the data property that I use to read .viewer from and set it as my current user.
Then the user can log-out, once they do that I clear the Container's user property setUser(undefined) (not showed in the code below, not important).
The problem occurred when I try to re-login: Call of refetch triggers the graphql http request but since it returns the same data that was returned during the previous initial login - useQuery() ignores it and does not update data. Well, technically there could not be an update, the data is the same. So my code setUser(viewer); does not getting executed for second time and user stucks on the login page.
const { data, refetch } = useQuery<Viewer>(VIEWER);
const viewer = data && data.viewer;
useEffect(() => {
if (viewer) {
setUser(viewer);
}
}, [ viewer ]);
That query with the same response ignore almost makes sense, so I tried different approach, with callbacks:
const { refetch } = useQuery<Viewer>(VIEWER, {
onCompleted: data => {
if (data.viewer) {
setUser(data.viewer);
}
}
});
Here I would totally expect Apollo to call the onCompleted callback, with the same data or not... but it does not do that. So I am kinda stuck with this - how do I make Apollo to react on my query's refetch so I could re-populate user in my Container's state?
This is a scenario where apollo's caches come handy.
Client
import { resolvers, typeDefs } from './resolvers';
let cache = new InMemoryCache()
const client = new ApolloClient({
cache,
link: new HttpLink({
uri: 'http://localhost:4000/graphql',
headers: {
authorization: localStorage.getItem('token'),
},
}),
typeDefs,
resolvers,
});
cache.writeData({
data: {
isLoggedIn: !!localStorage.getItem('token'),
cartItems: [],
},
})
LoginPage
const IS_LOGGED_IN = gql`
query IsUserLoggedIn {
isLoggedIn #client
}
`;
function IsLoggedIn() {
const { data } = useQuery(IS_LOGGED_IN);
return data.isLoggedIn ? <Pages /> : <Login />;
}
onLogin
function Login() {
const { data, refetch } = useQuery(LOGIN_QUERY);
let viewer = data && data.viewer
if (viewer){
localStorage.setItem('token',viewer.token)
}
// rest of the stuff
}
onLogout
onLogout={() => {
client.writeData({ data: { isLoggedIn: false } });
localStorage.clear();
}}
For more information regarding management of local state. Check this out.
Hope this helps!

apollo-link-state and how do I access the local state / cache?

Perhaps I am just not getting what apollo-link-state does, but I figured if I had a "default" value, THAT would show up in my props via the Provider. Yet, I can't locate it. How do you access the "cache" or local state?
I have:
import { ApolloClient, createNetworkInterface } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { withClientState } from 'apollo-link-state';
import dataIdFromObject from './dataIdFromObject';
const defaults = {
NAME: 'Biff'
};
const resolvers = {};
const cache = new InMemoryCache();
const stateLink = withClientState({ cache, resolvers, defaults });
const apolloClient = new ApolloClient({
cache,
link: stateLink,
networkInterface: createNetworkInterface({
uri: `${config.url}/graphql`,
opts: {
credentials: 'include'
}
}),
addTypename: true,
dataIdFromObject
});
I am passing in an empty object for my resolvers as it absolutely makes no sense to replicate all of reducers that are in the backend. I figured that I'd see the "name: Biff" in the props. Nope.
The store is my "redux" store and is not part of this question. I figured with that "client" prop, I'd see my default. Nope.
<ApolloProvider store={this.props.store} client={apolloClient}>
when I log my props in a child component, no sign of cache or "name: Biff" anywhere. How do I get at this local state in my child components. If I update it with a mutation, I should see my components rerender and having access to that new updated local state...but.. where is it?
As outlined in the docs, you query your local state just like you query a remote server, except you tack on a #client directive to let Apollo know that you're requesting something through apollo-link-state. So we need a GraphQL query, wrapped with a graphql-tag template literal tag:
const GET_NAME = gql`
query {
NAME #client
}
`
And we use it just like any other query:
<Query query={GET_NAME}>
{({ loading, error, data }) => {
// render appropriate component depending on loading/error state
}}
</Query>
While Apollo exposes methods for reading and writing to the cache, these should only be used in the context of creating mutations for your local state. Querying the cache should be done through the Query component and actually mutating the cache should be done through the Mutation component. You can read more about writing your own mutations in the docs.

How to pass mocked executable schema to Apollo Client?

The Mocking example for Apollo GraphQL has the following code (see below).
The interesting thing is the last line - they create and execute the graphql query. But you usually need to create ApolloClient object. I can't figure out how to do that.
The ApolloClient expect the NetworkingInterface as an argument not the executable schema.
So, is there a way to create ApolloClient from the executable schema, without NetworkingInterface?
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import { graphql } from 'graphql';
// Fill this in with the schema string
const schemaString = `...`;
// Make a GraphQL schema with no resolvers
const schema = makeExecutableSchema({ typeDefs: schemaString });
// Add mocks, modifies schema in place
addMockFunctionsToSchema({ schema });
const query = `
query tasksForUser {
user(id: 6) { id, name }
}
`;
graphql(schema, query).then((result) => console.log('Got result', result));
The following is lifted from a docs PR written by magbicaleman on GitHub, based on our blog post:
You can easily do this with the apollo-test-utils, like so:
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import { mockNetworkInterfaceWithSchema } from 'apollo-test-utils';
import { typeDefs } from './schema';
// Create GraphQL schema object
const schema = makeExecutableSchema({ typeDefs });
// Add mocks
addMockFunctionsToSchema({ schema });
// Create network interface
const mockNetworkInterface = mockNetworkInterfaceWithSchema({ schema });
// Initialize client
const client = new ApolloClient({
networkInterface: mockNetworkInterface,
});
Now you can use the client instance as normal!
In Apollo client v2, networkInterface has been replaced with link for the network layer (see the client docs here).
apollo-test-utils hasn't been updated for Apollo client v2, and based on conversations from github, it seems the current recommendation is to use apollo-link-schema:
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { SchemaLink } from 'apollo-link-schema';
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import { typeDefs } from './schema';
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });
const graphqlClient = new ApolloClient({
cache: new InMemoryCache(),
link: new SchemaLink({ schema })
});
Then you just need to inject the client into whatever you're testing!

Resources