Skip to content

Commit a89db66

Browse files
authored
Merge pull request #4189 from ffluk3/throw-on-override
Provide 'throw' option for `overrideExisting`
2 parents 0ab713e + a060b45 commit a89db66

File tree

5 files changed

+111
-5
lines changed

5 files changed

+111
-5
lines changed

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

+8-1
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

+3-1
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

+8-1
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

+6-2
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,14 @@ 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+
if (inject.overrideExisting === 'throw') {
360+
throw new Error(
361+
`called \`injectEndpoints\` to override already-existing endpointName ${endpointName} without specifying \`overrideExisting: true\``,
362+
)
363+
} else if (
360364
typeof process !== 'undefined' &&
361365
process.env.NODE_ENV === 'development'
362366
) {
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)