Code Generation
RTK Query's API and architecture is oriented around declaring API endpoints up front. This lends itself well to automatically generating API slice definitions from external API schema definitions, such as OpenAPI and GraphQL.
We have early previews of code generation capabilities available as separate tools.
GraphQL
We provide a Plugin for GraphQL Codegen. You can find the documentation to that on the graphql-codegen homepage.
For a full example on how to use it, you can see this example project.
OpenAPI
We provide a package for RTK Query code generation from OpenAPI schemas. It is published as @rtk-query/codegen-openapi and you can find the source code at packages/rtk-query-codegen-openapi.
Usage
Create an empty api using createApi like
// Or from '@reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// initialize an empty api service that we'll inject endpoints into later as needed
export const emptySplitApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: () => ({}),
})
Generate a config file (json, js or ts) with contents like
import type { ConfigFile } from '@rtk-query/codegen-openapi'
const config: ConfigFile = {
schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
apiFile: './src/store/emptyApi.ts',
apiImport: 'emptySplitApi',
outputFile: './src/store/petApi.ts',
exportName: 'petApi',
hooks: true,
}
export default config
and then call the code generator:
npx @rtk-query/codegen-openapi openapi-config.ts
Generating tags
If your OpenAPI specification uses tags, you can specify the tag option to the codegen.
That will result in all generated endpoints having providesTags/invalidatesTags declarations for the tags of their respective operation definition.
Note that this will only result in string tags with no ids, so it might lead to scenarios where too much is invalidated and unnecessary requests are made on mutation.
In that case you have two options:
- Use
endpointOverridesto customize tags for specific endpoints during code generation - Use
enhanceEndpointsafter generation to manually add more specificprovidesTags/invalidatesTagswith IDs
Programmatic usage
import { generateEndpoints } from '@rtk-query/codegen-openapi'
const api = await generateEndpoints({
apiFile: './fixtures/emptyApi.ts',
schemaFile: resolve(__dirname, 'fixtures/petstore.json'),
filterEndpoints: ['getPetById', 'addPet'],
hooks: true,
})
With Node.js Child process
import { exec } from 'node:child_process'
const cliPath = require.resolve('@rtk-query/codegen-openapi/cli')
// you can also use esbuild-runner (esr) or ts-node instead of tsx
exec(`tsx ${cliPath} config.ts`)
Config file options
Simple usage
interface SimpleUsage {
apiFile: string
schemaFile: string
apiImport?: string
exportName?: string
argSuffix?: string
operationNameSuffix?: string
responseSuffix?: string
hooks?:
| boolean
| { queries: boolean; lazyQueries: boolean; mutations: boolean }
tag?: boolean
outputFile: string
filterEndpoints?:
| string
| RegExp
| EndpointMatcherFunction
| Array<string | RegExp | EndpointMatcherFunction>
endpointOverrides?: EndpointOverrides[]
flattenArg?: boolean
}
export type EndpointOverrides = {
pattern: EndpointMatcher
} & AtLeastOneOf<{
type: 'mutation' | 'query'
parameterFilter: ParameterMatcher
providesTags: string[]
invalidatesTags: string[]
}>
useEnumType?: boolean
outputRegexConstants?: boolean
httpResolverOptions?: SwaggerParser.HTTPResolverOptions
}
export type EndpointMatcherFunction = (
operationName: string,
operationDefinition: OperationDefinition,
) => boolean
Filtering endpoints
If you only want to include a few endpoints, you can use the filterEndpoints config option to filter your endpoints.
Note that endpoints are transformed to camel case. For example, login_user will become loginUser.
filterEndpoints will be checked against this camel case version of the endpoint.
const filteredConfig: ConfigFile = {
// ...
// should only have endpoints loginUser, placeOrder, getOrderById, deleteOrder
filterEndpoints: ['loginUser', /Order/],
}
Endpoint overrides
If an endpoint is generated as a mutation instead of a query or the other way round, you can override that:
const withOverride: ConfigFile = {
// ...
endpointOverrides: [
{
pattern: 'loginUser',
type: 'mutation',
},
],
}
You can also filter the parameters that are included for an endpoint, as long as they aren't a path parameter. This filter is of type ParameterMatcher. For example, to only include parameters that begin with "x-" for the 'loginUser' endpoint, see the below example.
const withOverride: ConfigFile = {
// ...
endpointOverrides: [
{
pattern: 'loginUser',
parameterFilter: /^x-/,
},
],
}
For more complex requirements, consider the other possible matchers, such as a ParameterMatcherFunction. The below example filters out any parameters that are in the header of the request.
const withOverride: ConfigFile = {
// ...
endpointOverrides: [
{
pattern: /.*/,
parameterFilter: (_name, parameter) => parameter.in !== 'header',
},
],
}
Overriding tags
You can override the providesTags and invalidatesTags generated for any endpoint, regardless of whether the global tag option is enabled:
const withTagOverrides: ConfigFile = {
// ...
tag: true, // or false - overrides work either way
endpointOverrides: [
{
// Override the tags for a specific query
pattern: 'getPetById',
providesTags: ['SinglePet', 'PetDetails'],
},
{
// Remove auto-generated tags by providing an empty array
pattern: 'deletePet',
invalidatesTags: [],
},
{
// Add both providesTags AND invalidatesTags to any endpoint
pattern: 'updatePet',
providesTags: ['LastUpdatedPet'],
invalidatesTags: ['Pet', 'PetList'],
},
],
}
Key behaviors:
- Tag overrides take precedence over auto-generated tags from the OpenAPI
tagsfield - You can use an empty array (
[]) to explicitly remove tags from an endpoint - Both
providesTagsandinvalidatesTagscan be set on any endpoint type (query or mutation) - Overrides work regardless of whether the global
tag: trueoption is set
This is useful when:
- The OpenAPI tags don't match your caching strategy
- You need more specific cache invalidation than the default tag generation provides
- A mutation should also provide tags (e.g., login returning user data)
- A query should also invalidate tags (e.g., polling that triggers cache updates)
When using tag overrides with tag: false, the overridden tags will be emitted in the generated code, but they won't be automatically added to addTagTypes. You may need to manually add your custom tags to the base API's tagTypes array.
Generating hooks
Setting hooks: true will generate useQuery and useMutation hook exports. If you also want useLazyQuery hooks generated or more granular control, you can also pass an object in the shape of: { queries: boolean; lazyQueries: boolean; mutations: boolean }.
Generating regex constants for schema patterns
If your OpenAPI schema uses the pattern keyword to specify regex validation on string properties, you can export these patterns as JavaScript regex constants by setting outputRegexConstants: true.
const config: ConfigFile = {
schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
apiFile: './src/store/emptyApi.ts',
outputFile: './src/store/petApi.ts',
outputRegexConstants: true,
}
For a schema with pattern-validated properties like:
User:
type: object
properties:
email:
type: string
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
phone:
type: string
pattern: '^\+?[1-9]\d{1,14}$'
The codegen will generate:
export const userEmailPattern =
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
export const userPhonePattern = /^\+?[1-9]\d{1,14}$/
These constants can be used for client-side validation to ensure consistency with API expectations.
Only string-type properties with non-empty pattern values will generate constants. The constant name follows the format {typeName}{propertyName}Pattern in camelCase.
Multiple output files
const config: ConfigFile = {
schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
apiFile: './src/store/emptyApi.ts',
outputFiles: {
'./src/store/user.ts': {
filterEndpoints: [/user/i],
},
'./src/store/order.ts': {
filterEndpoints: [/order/i],
},
'./src/store/pet.ts': {
filterEndpoints: [/pet/i],
},
},
}
Custom HTTP resolver options
If you need to customize the HTTP request issued to your server, you user the httpResolverOptions option. This object is passed directly to the SwaggerParser instance that fetches the OpenAPI schema.
For example, you can pass custom headers or set a custom request timeout.
const config: ConfigFile = {
schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
apiFile: './src/store/emptyApi.ts',
outputFile: './src/store/petApi.ts',
httpResolverOptions: {
timeout: 30_000,
headers: {
Accept: 'application/json',
Authorization: 'Basic cmVkdXgtdG9vbGtpdDppcy1ncmVhdA==',
},
},
}