Reputation: 6004
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!
Upvotes: 1
Views: 739
Reputation: 6004
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)
}
}
`;
Upvotes: 0
Reputation: 8428
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.
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
}
`;
Upvotes: 1