Skip to content

Commit 2718c97

Browse files
committed
Provide 'throw' options for overrideExisting
This allows teams to configure the injectEndpoints functionality to throw an error when an endpoint is overridden. This can help in situations where overridding a given endpoint is not desired, and will result in unexpected functionality.
1 parent 0ab713e commit 2718c97

File tree

5 files changed

+111
-8
lines changed

5 files changed

+111
-8
lines changed

docs/rtk-query/api/created-api/code-splitting.mdx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ const injectEndpoints = (endpointOptions: InjectedEndpointOptions) =>
2525

2626
interface InjectedEndpointOptions {
2727
endpoints: (build: EndpointBuilder) => NewEndpointDefinitions
28-
overrideExisting?: boolean
28+
/**
29+
* Optionally allows endpoints to be overridden if defined by multiple `injectEndpoints` calls.
30+
*
31+
* If set to `true`, will override existing endpoints with the new definition.
32+
* If set to `'throw'`, will throw an error if an endpoint is redefined with a different definition.
33+
* If set to `false` (or unset), will not override existing endpoints with the new definition, and log a warning in development.
34+
*/
35+
overrideExisting?: boolean | 'throw'
2936
}
3037
```
3138

docs/rtk-query/usage/code-splitting.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,7 @@ export const { useExampleQuery } = extendedApi
5656
```
5757

5858
:::tip
59-
If you inject an endpoint that already exists and don't explicitly specify `overrideExisting: true`, the endpoint will not be overridden. In development mode, you will get a warning about this.
59+
If you inject an endpoint that already exists and don't explicitly specify `overrideExisting: true`, the endpoint
60+
will not be overridden. In development mode, you will get a warning about this if `overrideExisting` is set to `false`,
61+
and an error will be throw if set to `'throw'`.
6062
:::

packages/toolkit/src/query/apiTypes.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,14 @@ export type Api<
8383
endpoints: (
8484
build: EndpointBuilder<BaseQuery, TagTypes, ReducerPath>,
8585
) => NewDefinitions
86-
overrideExisting?: boolean
86+
/**
87+
* Optionally allows endpoints to be overridden if defined by multiple `injectEndpoints` calls.
88+
*
89+
* If set to `true`, will override existing endpoints with the new definition.
90+
* If set to `'throw'`, will throw an error if an endpoint is redefined with a different definition.
91+
* If set to `false` (or unset), will not override existing endpoints with the new definition, and log a warning in development.
92+
*/
93+
overrideExisting?: boolean | 'throw'
8794
}): Api<
8895
BaseQuery,
8996
Definitions & NewDefinitions,

packages/toolkit/src/query/createApi.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -353,16 +353,17 @@ export function buildCreateApi<Modules extends [Module<any>, ...Module<any>[]]>(
353353
evaluatedEndpoints,
354354
)) {
355355
if (
356-
!inject.overrideExisting &&
356+
inject.overrideExisting !== true &&
357357
endpointName in context.endpointDefinitions
358358
) {
359-
if (
359+
const errorMessage = `called \`injectEndpoints\` to override already-existing endpointName ${endpointName} without specifying \`overrideExisting: true\``
360+
if (inject.overrideExisting === 'throw') {
361+
throw new Error(errorMessage)
362+
} else if (
360363
typeof process !== 'undefined' &&
361364
process.env.NODE_ENV === 'development'
362365
) {
363-
console.error(
364-
`called \`injectEndpoints\` to override already-existing endpointName ${endpointName} without specifying \`overrideExisting: true\``,
365-
)
366+
console.error(errorMessage)
366367
}
367368

368369
continue
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
2+
import { vi } from 'vitest'
3+
4+
const api = createApi({
5+
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
6+
endpoints: () => ({}),
7+
})
8+
9+
describe('injectEndpoints', () => {
10+
test("query: overridding with `overrideEndpoints`='throw' throws an error", async () => {
11+
const extended = api.injectEndpoints({
12+
endpoints: (build) => ({
13+
injected: build.query<unknown, string>({
14+
query: () => '/success',
15+
}),
16+
}),
17+
})
18+
19+
expect(() => {
20+
extended.injectEndpoints({
21+
overrideExisting: 'throw',
22+
endpoints: (build) => ({
23+
injected: build.query<unknown, string>({
24+
query: () => '/success',
25+
}),
26+
}),
27+
})
28+
}).toThrowError(
29+
new Error(
30+
`called \`injectEndpoints\` to override already-existing endpointName injected without specifying \`overrideExisting: true\``,
31+
),
32+
)
33+
})
34+
35+
test('query: overridding an endpoint with `overrideEndpoints`=false does nothing in production', async () => {
36+
const consoleMock = vi.spyOn(console, 'error').mockImplementation(() => {})
37+
38+
process.env.NODE_ENV = 'development'
39+
40+
const extended = api.injectEndpoints({
41+
endpoints: (build) => ({
42+
injected: build.query<unknown, string>({
43+
query: () => '/success',
44+
}),
45+
}),
46+
})
47+
48+
extended.injectEndpoints({
49+
overrideExisting: false,
50+
endpoints: (build) => ({
51+
injected: build.query<unknown, string>({
52+
query: () => '/success',
53+
}),
54+
}),
55+
})
56+
57+
expect(consoleMock).toHaveBeenCalledWith(
58+
`called \`injectEndpoints\` to override already-existing endpointName injected without specifying \`overrideExisting: true\``,
59+
)
60+
})
61+
62+
test('query: overridding with `overrideEndpoints`=false logs an error in development', async () => {
63+
const consoleMock = vi.spyOn(console, 'error').mockImplementation(() => {})
64+
65+
process.env.NODE_ENV = 'production'
66+
67+
const extended = api.injectEndpoints({
68+
endpoints: (build) => ({
69+
injected: build.query<unknown, string>({
70+
query: () => '/success',
71+
}),
72+
}),
73+
})
74+
75+
extended.injectEndpoints({
76+
overrideExisting: false,
77+
endpoints: (build) => ({
78+
injected: build.query<unknown, string>({
79+
query: () => '/success',
80+
}),
81+
}),
82+
})
83+
84+
expect(consoleMock).not.toHaveBeenCalled()
85+
})
86+
})

0 commit comments

Comments
 (0)