Skip to content

Commit f258651

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

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-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: { 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: { 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

+6
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,8 @@ interface RequestContext {
840840

841841
/** An array of the responses from each fetched URL */
842842
responses: Response[];
843+
844+
signal?: AbortSignal;
843845
}
844846

845847
const addTypeToNode = (node, typename) => {
@@ -924,6 +926,7 @@ const resolver: Resolver = async (
924926
fieldNameNormalizer: linkLevelNameNormalizer,
925927
fieldNameDenormalizer: linkLevelNameDenormalizer,
926928
serializers,
929+
signal,
927930
responseTransformer,
928931
} = context;
929932

@@ -1060,6 +1063,7 @@ const resolver: Resolver = async (
10601063
// Only set credentials if they're non-null as some browsers throw an exception:
10611064
// https://github.com/apollographql/apollo-link-rest/issues/121#issuecomment-396049677
10621065
...(credentials ? { credentials } : {}),
1066+
...(signal ? { signal } : {}),
10631067
};
10641068
const requestUrl = `${endpointOption.uri}${pathWithParams}`;
10651069

@@ -1332,6 +1336,7 @@ export class RestLink extends ApolloLink {
13321336

13331337
const credentials: RequestCredentials =
13341338
context.credentials || this.credentials;
1339+
const signal: AbortSignal | undefined = context.signal;
13351340

13361341
const queryWithTypename = addTypenameToDocument(query);
13371342

@@ -1355,6 +1360,7 @@ export class RestLink extends ApolloLink {
13551360
fragmentDefinitions,
13561361
typePatcher: this.typePatcher,
13571362
serializers: this.serializers,
1363+
signal,
13581364
responses: [],
13591365
responseTransformer: this.responseTransformer,
13601366
};

0 commit comments

Comments
 (0)