Skip to content

Commit d9a9106

Browse files
Timeout triggered on createApi endpoint now correctly returns TIMEOUT_ERROR instead of generic AbortError.
1 parent bc0bf0f commit d9a9106

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

packages/toolkit/src/query/fetchBaseQuery.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export function fetchBaseQuery({
213213
)
214214
}
215215
return async (arg, api) => {
216-
const { signal, getState, extra, endpoint, forced, type } = api
216+
const { getState, extra, endpoint, forced, type } = api
217217
let meta: FetchBaseQueryMeta | undefined
218218
let {
219219
url,
@@ -224,6 +224,14 @@ export function fetchBaseQuery({
224224
timeout = defaultTimeout,
225225
...rest
226226
} = typeof arg == 'string' ? { url: arg } : arg
227+
228+
let abortController: AbortController | undefined, signal = api.signal
229+
if (timeout) {
230+
abortController = new AbortController()
231+
api.signal.addEventListener('abort', abortController.abort)
232+
signal = abortController.signal
233+
}
234+
227235
let config: RequestInit = {
228236
...baseFetchOptions,
229237
signal,
@@ -272,10 +280,10 @@ export function fetchBaseQuery({
272280
let response,
273281
timedOut = false,
274282
timeoutId =
275-
timeout &&
283+
abortController &&
276284
setTimeout(() => {
277285
timedOut = true
278-
api.abort()
286+
abortController!.abort()
279287
}, timeout)
280288
try {
281289
response = await fetchFn(request)
@@ -289,6 +297,7 @@ export function fetchBaseQuery({
289297
}
290298
} finally {
291299
if (timeoutId) clearTimeout(timeoutId)
300+
abortController?.signal.removeEventListener('abort', abortController.abort)
292301
}
293302
const responseClone = response.clone()
294303

packages/toolkit/src/query/tests/createApi.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,3 +1128,38 @@ describe('custom serializeQueryArgs per endpoint', () => {
11281128
})
11291129
})
11301130
})
1131+
1132+
describe('timeout behavior', () => {
1133+
test('triggers TIMEOUT_ERROR', async () => {
1134+
const api = createApi({
1135+
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com', timeout: 5 }),
1136+
endpoints: (build) => ({
1137+
query: build.query<unknown, void>({
1138+
query: () => '/success',
1139+
}),
1140+
}),
1141+
})
1142+
1143+
const storeRef = setupApiStore(api, undefined, {
1144+
withoutTestLifecycles: true,
1145+
})
1146+
1147+
server.use(
1148+
http.get(
1149+
'https://example.com/success',
1150+
async () => {
1151+
await delay(10)
1152+
return HttpResponse.json({ value: 'failed' }, { status: 500 })
1153+
},
1154+
{ once: true },
1155+
),
1156+
)
1157+
1158+
const result = await storeRef.store.dispatch(api.endpoints.query.initiate())
1159+
1160+
expect(result?.error).toEqual({
1161+
status: 'TIMEOUT_ERROR',
1162+
error: expect.stringMatching(/^AbortError:/),
1163+
})
1164+
})
1165+
})

0 commit comments

Comments
 (0)