Skip to content

Commit f1fa6ac

Browse files
Add support for AbortController signal passed via context
1 parent cde5f56 commit f1fa6ac

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

Diff for: src/__tests__/restLink.ts

+72
Original file line numberDiff line numberDiff line change
@@ -4207,4 +4207,76 @@ describe('Playing nice with others', () => {
42074207
authors: { message: 'Your query was bad and you should feel bad!' },
42084208
});
42094209
});
4210+
4211+
it('should fulfill fetch when an AbortController signal is passed but never aborted', done => {
4212+
fetchMock.get('/api/posts', posts);
4213+
const link = new RestLink({ uri: '/api' });
4214+
4215+
const query = gql`
4216+
query {
4217+
people @rest(type: "[Post]", path: "/posts") {
4218+
title
4219+
}
4220+
}
4221+
`;
4222+
4223+
const controller = new AbortController();
4224+
const reqPromise = toPromise<Result>(
4225+
execute(link, {
4226+
operationName: 'abortQuery',
4227+
query,
4228+
context: { fetchOptions: { signal: controller.signal } },
4229+
}),
4230+
);
4231+
4232+
const timeout = setTimeout(() => {
4233+
done('timeout should never run');
4234+
}, 100);
4235+
4236+
return reqPromise.then(res => {
4237+
clearTimeout(timeout);
4238+
expect(res.data).toEqual({
4239+
people: [
4240+
{ title: 'Love apollo', __typename: 'Post' },
4241+
{ title: 'Respect apollo', __typename: 'Post' },
4242+
],
4243+
});
4244+
done();
4245+
});
4246+
});
4247+
4248+
it('should cancel fetch when an AbortController signal is passed and aborted', done => {
4249+
fetchMock.get('/api/posts', posts);
4250+
const link = new RestLink({ uri: '/api' });
4251+
4252+
const query = gql`
4253+
query {
4254+
people @rest(ftype: "[Post]", path: "/posts") {
4255+
title
4256+
}
4257+
}
4258+
`;
4259+
4260+
const controller = new AbortController();
4261+
const reqPromise = toPromise<Result>(
4262+
execute(link, {
4263+
operationName: 'abortQuery',
4264+
query,
4265+
context: { fetchOptions: { signal: controller.signal } },
4266+
}),
4267+
);
4268+
controller.abort();
4269+
4270+
let reqData = null;
4271+
const timeout = setTimeout(() => {
4272+
expect(reqData).toBeNull();
4273+
done();
4274+
}, 100);
4275+
4276+
return reqPromise.then(res => {
4277+
clearTimeout(timeout);
4278+
reqData = res.data;
4279+
done('fetch request should not resolve');
4280+
});
4281+
});
42104282
});

Diff for: src/restLink.ts

+12
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,11 @@ interface LinkChainContext {
814814

815815
/** An array of the responses from each fetched URL, useful for accessing headers in earlier links */
816816
restResponses?: Response[];
817+
818+
/** Overrides some fetch options arguments passed to the fetch call */
819+
fetchOptions?: {
820+
signal?: AbortSignal;
821+
};
817822
}
818823

819824
/** Context passed via graphql() to our resolver */
@@ -840,6 +845,8 @@ interface RequestContext {
840845

841846
/** An array of the responses from each fetched URL */
842847
responses: Response[];
848+
849+
signal?: AbortSignal;
843850
}
844851

845852
const addTypeToNode = (node, typename) => {
@@ -924,6 +931,7 @@ const resolver: Resolver = async (
924931
fieldNameNormalizer: linkLevelNameNormalizer,
925932
fieldNameDenormalizer: linkLevelNameDenormalizer,
926933
serializers,
934+
signal,
927935
responseTransformer,
928936
} = context;
929937

@@ -1060,6 +1068,7 @@ const resolver: Resolver = async (
10601068
// Only set credentials if they're non-null as some browsers throw an exception:
10611069
// https://github.com/apollographql/apollo-link-rest/issues/121#issuecomment-396049677
10621070
...(credentials ? { credentials } : {}),
1071+
...(signal ? { signal } : {}),
10631072
};
10641073
const requestUrl = `${endpointOption.uri}${pathWithParams}`;
10651074

@@ -1332,6 +1341,8 @@ export class RestLink extends ApolloLink {
13321341

13331342
const credentials: RequestCredentials =
13341343
context.credentials || this.credentials;
1344+
const signal: AbortSignal | undefined =
1345+
context.fetchOptions != null ? context.fetchOptions.signal : undefined;
13351346

13361347
const queryWithTypename = addTypenameToDocument(query);
13371348

@@ -1355,6 +1366,7 @@ export class RestLink extends ApolloLink {
13551366
fragmentDefinitions,
13561367
typePatcher: this.typePatcher,
13571368
serializers: this.serializers,
1369+
signal,
13581370
responses: [],
13591371
responseTransformer: this.responseTransformer,
13601372
};

0 commit comments

Comments
 (0)