Skip to content

Commit

Permalink
Prevent @client fields from being passed into the Link chain
Browse files Browse the repository at this point in the history
Apollo Client 2.x allowed `@client` fields to be passed into the
`link` chain if `resolvers` were not set in the constructor. This
allowed `@client` fields to be passed into Links like
`apollo-link-state`. Apollo Client 3 enforces that `@client` fields
are local only, meaning they are no longer passed into the `link`
chain, under any circumstances. We're making this change as we're
slowly starting to deprecate the Local State API (and local
resolvers) in favor of the new Type Policies API. Since we want
people to be able to use the Type Policies API (and `@client`)
without local resolvers, we don't want to force them to always
define `resolvers: {}` in their `ApolloClient` constructor, to
prevent `@client` fields from being passed into the `link` chain.

If anyone wants to preserve this behavior in AC 3, they can
consider using their own directive (instead of `@client`), and
using that directive in the `link` chain.
  • Loading branch information
hwillson committed Feb 25, 2020
1 parent 07e85cb commit b93016a
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 64 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
- **[BREAKING?]** Remove `fixPolyfills.ts`, except when bundling for React Native. If you have trouble with `Map` or `Set` operations due to frozen key objects in React Native, either update React Native to version 0.59.0 (or 0.61.x, if possible) or investigate why `fixPolyfills.native.js` is not included in your bundle. <br/>
[@benjamn](https://github.com/benjamn) in [#5962](https://github.com/apollographql/apollo-client/pull/5962)

- **[BREAKING]** Apollo Client 2.x allowed `@client` fields to be passed into the `link` chain if `resolvers` were not set in the constructor. This allowed `@client` fields to be passed into Links like `apollo-link-state`. Apollo Client 3 enforces that `@client` fields are local only, meaning they are no longer passed into the `link` chain, under any circumstances. <br/>
[@hwillson](https://github.com/hwillson) in [#5982](https://github.com/apollographql/apollo-client/pull/5982)

- `InMemoryCache` now supports tracing garbage collection and eviction. Note that the signature of the `evict` method has been simplified in a potentially backwards-incompatible way. <br/>
[@benjamn](https://github.com/benjamn) in [#5310](https://github.com/apollographql/apollo-client/pull/5310)

Expand Down
17 changes: 7 additions & 10 deletions src/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,16 @@ export class ApolloClient<TCacheShape> implements DataProxy {
let { link } = options;

if (!link) {
if (uri) {
link = new HttpLink({ uri, credentials, headers });
} else if (resolvers) {
link = ApolloLink.empty();
}
link = uri
? new HttpLink({ uri, credentials, headers })
: ApolloLink.empty();
}

if (!link || !cache) {
if (!cache) {
throw new InvariantError(
"To initialize Apollo Client, you must specify 'uri' or 'link' and " +
"'cache' properties in the options object. \n" +
"For more information, please visit: " +
"https://www.apollographql.com/docs/react/"
"To initialize Apollo Client, you must specify a 'cache' property " +
"in the options object. \n" +
"For more information, please visit: https://go.apollo.dev/c/docs"
);
}

Expand Down
14 changes: 8 additions & 6 deletions src/__tests__/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ describe('ApolloClient', () => {
window.fetch = oldFetch;
});

it('will throw an error if `uri` or `link` is not passed in', () => {
expect(() => {
new ApolloClient({ cache: new InMemoryCache() } as any);
}).toThrowErrorMatchingSnapshot();
});

it('will throw an error if cache is not passed in', () => {
expect(() => {
new ApolloClient({ link: ApolloLink.empty() } as any);
Expand Down Expand Up @@ -57,6 +51,14 @@ describe('ApolloClient', () => {
});
expect((client.link as HttpLink).options.uri).toEqual(uri2);
});

it('should create an empty Link if `uri` and `link` are not provided', () => {
const client = new ApolloClient({
cache: new InMemoryCache(),
});
expect(client.link).toBeDefined();
expect(client.link instanceof ApolloLink).toBeTruthy();
});
});

describe('readQuery', () => {
Expand Down
9 changes: 2 additions & 7 deletions src/__tests__/__snapshots__/ApolloClient.ts.snap
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ApolloClient constructor will throw an error if \`uri\` or \`link\` is not passed in 1`] = `
"To initialize Apollo Client, you must specify 'uri' or 'link' and 'cache' properties in the options object.
For more information, please visit: https://www.apollographql.com/docs/react/"
`;

exports[`ApolloClient constructor will throw an error if cache is not passed in 1`] = `
"To initialize Apollo Client, you must specify 'uri' or 'link' and 'cache' properties in the options object.
For more information, please visit: https://www.apollographql.com/docs/react/"
"To initialize Apollo Client, you must specify a 'cache' property in the options object.
For more information, please visit: https://go.apollo.dev/c/docs"
`;

exports[`ApolloClient write then read will not use a default id getter if either _id or id is present when __typename is not also present 1`] = `
Expand Down
34 changes: 0 additions & 34 deletions src/__tests__/local-state/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,40 +35,6 @@ describe('General functionality', () => {
});
});

// TODO The functionality tested here should be removed (along with the test)
// once apollo-link-state is fully deprecated.
it('should strip @client fields only if client resolvers specified', async () => {
const query = gql`
{
field @client
}
`;

const client = new ApolloClient({
cache: new InMemoryCache(),
link: new ApolloLink(operation => {
expect(hasDirectives(['client'], operation.query)).toBe(true);
return Observable.of({ data: { field: 'local' } });
}),
});

const { warn } = console;
const messages: string[] = [];
console.warn = (message: string) => messages.push(message);
try {
const result = await client.query({ query });
expect(result.data).toEqual({ field: 'local' });
expect(messages).toEqual([
'Found @client directives in a query but no ApolloClient resolvers ' +
'were specified. This means ApolloClient local resolver handling ' +
'has been disabled, and @client directives will be passed through ' +
'to your link chain.',
]);
} finally {
console.warn = warn;
}
});

it('should not interfere with server introspection queries', () => {
const query = gql`
${getIntrospectionQuery()}
Expand Down
8 changes: 1 addition & 7 deletions src/core/LocalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,19 +165,13 @@ export class LocalState<TCacheShape> {
if (this.resolvers) {
return document;
}
invariant.warn(
'Found @client directives in a query but no ApolloClient resolvers ' +
'were specified. This means ApolloClient local resolver handling ' +
'has been disabled, and @client directives will be passed through ' +
'to your link chain.',
);
}
return null;
}

// Server queries are stripped of all @client based selection sets.
public serverQuery(document: DocumentNode) {
return this.resolvers ? removeClientSetsFromDocument(document) : document;
return removeClientSetsFromDocument(document);
}

public prepareContext(context?: Record<string, any>) {
Expand Down

0 comments on commit b93016a

Please sign in to comment.