Skip to content

@kubb/plugin-solid-query

Generate SolidJS Query hooks from your OpenAPI schema.

Installation

shell
bun add -d @kubb/plugin-solid-query
shell
pnpm add -D @kubb/plugin-solid-query
shell
npm install --save-dev @kubb/plugin-solid-query
shell
yarn add -D @kubb/plugin-solid-query

Options

output

Specify the export location for the files and define the behavior of the output.

output.path

Path to the output folder or file that contains the generated code.

TIP

if output.path is a file, group cannot be used.

Type:string
Required:true
Default:'hooks'

output.barrelType

Specify what to export and optionally disable barrel file generation.

TIP

Using propagate will prevent a plugin from creating a barrel file, but it will still propagate, allowing output.barrelType to export the specific function or type.

Type:'all' | 'named' | 'propagate' | false
Required:false
Default:'named'
typescript
export * from "./gen/petService.ts"
typescript
export { PetService } from "./gen/petService.ts"
typescript
typescript

output.banner

Add a banner comment at the top of every generated file.

Type:string | (oas: Oas) => string
Required:false

Add a footer comment at the end of every generated file.

Type:string | (oas: Oas) => string
Required:false

output.override

Whether Kubb overrides existing external files that can be generated if they already exist.

Type:boolean
Required:false
Default:false

contentType

Define which content type to use.

By default, Kubb uses the first JSON-valid media type.

Type:'application/json' | (string & {})
Required:false

group

Grouping combines files in a folder based on a specific type.

For example, with this configuration:

kubb.config.ts
typescript
group: {
  type: 'tag',
  name({ group }){
    return `${group}Controller`
  }
}

This generates the following structure:

.
├── src/
│   └── petController/
│   │   ├── addPet.ts
│   │   └── getPet.ts
│   └── storeController/
│       ├── createStore.ts
│       └── getStoreById.ts
├── petStore.yaml
├── kubb.config.ts
└── package.json

group.type

Specify the property to group files by.

Type:'tag'
Required:true
  • 'tag': Uses the first tag from operation.getTags().at(0)?.name

group.name

Return the name of a group based on the group name, this will be used for the file and name generation.

Type:(context: GroupContext) => string
Required:false
Default:(ctx) => '${ctx.group}Controller'

client

client.importPath

Path to the client used for API calls. Supports both relative and absolute paths.

When to use importPath

Use importPath when you want to:

  • Customize the HTTP client: Provide your own client implementation with custom configurations (e.g., baseURL, headers, interceptors)
  • Add authentication: Include authentication tokens or other security mechanisms in your client
  • Override default behavior: Replace the default Kubb client with your own implementation

Default behavior

When importPath is not specified:

  • If bundle: false (default): Uses @kubb/plugin-client/clients/${client} where client is either axios or fetch
  • If bundle: true: Bundles the client into .kubb/fetch.ts

Import structure

Generated code imports:

  • Client as default import
  • Types as named type imports

Example for axios client:

typescript
/**
 * Generated by Kubb (https://kubb.dev/).
 * Do not edit manually.
 */
import client from '${client.importPath}'
import type { RequestConfig, ResponseErrorConfig } from '${client.importPath}'
// ... rest of generated file

IMPORTANT

When using importPath with query plugins such as @kubb/plugin-react-query, @kubb/plugin-vue-query, @kubb/plugin-svelte-query, @kubb/plugin-solid-query, or @kubb/plugin-swr, the generated hooks also import type Client from the custom module:

typescript
import type { Client, RequestConfig, ResponseErrorConfig } from '@/lib/client'

Your custom client module must export these three types. If any of them is missing, TypeScript will report an unresolvable import error.

client.ts
typescript
export type RequestConfig<TData = unknown> = {
  url?: string
  method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE'
  params?: object
  data?: TData | FormData
  responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
  signal?: AbortSignal
  headers?: HeadersInit
}

export type ResponseConfig<TData = unknown> = {
  data: TData
  status: number
  statusText: string
}

export type ResponseErrorConfig<TError = unknown> = TError

// The Client type alias is required when using query plugins
export type Client = <TData, _TError = unknown, TVariables = unknown>(
  config: RequestConfig<TVariables>
) => Promise<ResponseConfig<TData>>

export const client: Client = async (config) => { /* ... */ }
export default client

Example configuration with custom client:

typescript
import { defineConfig } from '@kubb/core'
import { pluginClient } from '@kubb/plugin-client'

export default defineConfig({
  // ...
  plugins: [
    pluginClient({
      importPath: './src/client.ts' // Path to your custom client
    }),
  ],
})

TIP

Learn more about defining a custom client here.

Type:string
Required:false

client.dataReturnType

Return type used when calling the client.

Type:'data' | 'full'
Required:false
Default:'data'
  • 'data' returns ResponseConfig[data].
  • 'full' returns ResponseConfig.
typescript
export async function getPetById<TData>(
  petId: GetPetByIdPathParams,
): Promise<ResponseConfig<TData>["data"]> {
  ...
}
typescript
export async function getPetById<TData>(
  petId: GetPetByIdPathParams,
): Promise<ResponseConfig<TData>> {
  ...
}

client.baseURL

Sets a custom base URL for all generated calls.

Type:string
Required:false

client.clientType

Specify whether to use function-based or class-based clients.

Type:'function' | 'class'
Required:false
Default:'function'

WARNING

This plugin is only compatible with clientType: 'function' (the default). If clientType: 'class' is detected, the plugin will automatically generate its own inline function-based client instead of importing from @kubb/plugin-client.

client.bundle

Controls whether the HTTP client runtime is copied into the generated .kubb directory.

Type:boolean
Required:false
Default:false
  • true adds a .kubb/fetch.ts file containing the selected client template (fetch or axios). Generated clients remain self-contained.
  • false keeps generated clients slim by importing the shared runtime from @kubb/plugin-client/clients/{client}.
  • Override this behavior by providing a custom client.importPath.

paramsType

Defines how parameters are passed to generated functions. Switch between object-style parameters and inline parameters.

Type:'object' | 'inline'
Required:false
Default:'inline'

TIP

When paramsType is set to 'object', pathParams will also be set to 'object'.

  • 'object' returns params and pathParams as an object.
  • 'inline' returns params as comma-separated params.
typescript
export async function deletePet(
  {
    petId,
    headers,
  }: {
    petId: DeletePetPathParams['petId']
    headers?: DeletePetHeaderParams
  },
  config: Partial<RequestConfig> = {},
) {
...
}
typescript
export async function deletePet(
  petId: DeletePetPathParams['petId'],
  headers?: DeletePetHeaderParams,
  config: Partial<RequestConfig> = {}
){
  ...
}

paramsCasing

Transform parameter names to a specific casing format for path, query, and header parameters in generated client code.

IMPORTANT

When using paramsCasing, ensure that @kubb/plugin-ts also has the same paramsCasing setting. This option automatically maps transformed parameter names back to their original API names in HTTP requests.

Type:'camelcase'
Required:false
Default:undefined
  • 'camelcase' transforms parameter names to camelCase
typescript
// Function parameters use camelCase
export async function deletePet(
  petId: DeletePetPathParams['petId'],  // ✓ camelCase
  headers?: DeletePetHeaderParams,
  config: Partial<RequestConfig> = {}
) {
  // Automatically maps back to original name for the API
  const pet_id = petId
  
  return fetch({
    method: 'DELETE',
    url: `/pet/${pet_id}`,  // Uses original API parameter name
    ...
  })
}
typescript
// Parameters use original API naming
export async function deletePet(
  pet_id: DeletePetPathParams['pet_id'],  // Original naming
  headers?: DeletePetHeaderParams,
  config: Partial<RequestConfig> = {}
) {
  return fetch({
    method: 'DELETE',
    url: `/pet/${pet_id}`,
    ...
  })
}

TIP

The client automatically generates mapping code to convert camelCase parameter names back to the original API format. You write code with developer-friendly camelCase names, but HTTP requests use the exact parameter names from your OpenAPI specification.

pathParamsType

Defines how pathParams are passed to generated functions.

Type:'object' | 'inline'
Required:false
Default:'inline'
  • 'object' returns pathParams as an object.
  • 'inline' returns pathParams as comma-separated params.
typescript
export async function getPetById (
  { petId }: GetPetByIdPathParams,
) {
  ...
}
typescript
export async function getPetById(
  petId: GetPetByIdPathParams,
) {
  ...
}

parser

Parser used before returning data.

Type:'client' | 'zod'
Required:false
Default:'client'
  • 'zod' uses @kubb/plugin-zod to parse data.
  • 'client' returns data without parsing.

queryKey

Customize the queryKey that will be used for the query.

The function receives an object with:

  • operation: The OpenAPI operation object with methods like getTags(), getOperationId(), etc.
  • schemas: An object containing operation schemas including pathParams, queryParams, request, response, etc.

WARNING

When using a string you need to use JSON.stringify.

Type:(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]
Required:false

Examples

Using tags and path parameters

Generate a queryKey with operation tags and path parameters:

typescript
import { defineConfig } from '@kubb/core'
import { pluginSolidQuery } from '@kubb/plugin-solid-query'

export default defineConfig({
  // ...
  plugins: [
    pluginSolidQuery({
      queryKey: ({ operation, schemas }) => {
        const tags = operation.getTags().map(tag => JSON.stringify(tag.name))
        const pathParams = schemas.pathParams?.keys || []
        return [...tags, ...pathParams]
      },
    }),
  ],
})

For a GET operation with tags ["user"] and path parameter username, this generates:

typescript
export const getUserByNameQueryKey = ({ username }: { username: GetUserByNamePathParams["username"] }) =>
  ["user", username] as const

Using the default transformer

You can extend the default queryKey transformer:

typescript
import { pluginSolidQuery } from '@kubb/plugin-solid-query'
import { QueryKey } from '@kubb/plugin-solid-query/components'

export default defineConfig({
  // ...
  plugins: [
    pluginSolidQuery({
      queryKey: (props) => {
        const defaultKeys = QueryKey.getTransformer(props)
        return [JSON.stringify('v5'), ...defaultKeys]
      },
    }),
  ],
})

This prepends a version to the default queryKey:

typescript
export const findPetsByTagsQueryKey = (params?: FindPetsByTagsQueryParams) =>
  ["v5", { url: '/pet/findByTags' }, ...(params ? [params] : [])] as const

Using operation ID

Create a simple queryKey using the operation ID:

typescript
import { pluginSolidQuery } from '@kubb/plugin-solid-query'

export default defineConfig({
  // ...
  plugins: [
    pluginSolidQuery({
      queryKey: ({ operation }) => {
        return [JSON.stringify(operation.getOperationId())]
      },
    }),
  ],
})

Conditional keys based on parameters

Include query parameters when they exist:

typescript
import { pluginSolidQuery } from '@kubb/plugin-solid-query'

export default defineConfig({
  // ...
  plugins: [
    pluginSolidQuery({
      queryKey: ({ operation, schemas }) => {
        const keys = [JSON.stringify(operation.getOperationId())]

        // Add path parameter values (without quotes, so they reference the variables)
        if (schemas.pathParams?.keys) {
          keys.push(...schemas.pathParams.keys)
        }

        // Add query params conditionally (the string gets embedded as code)
        if (schemas.queryParams?.name) {
          keys.push('...(params ? [params] : [])')
        }

        return keys
      },
    }),
  ],
})

query

Override some useQuery behaviors.
To disable the creation of hooks pass false, this will result in only creating queryOptions.

Type:Query
Required:false
Query
typescript
type Query = {
  methods: Array<HttpMethod>
  importPath?: string
} | false

query.methods

Define which HttpMethods can be used for queries

Type:Array<HttpMethod>
Required:['get']

query.importPath

Path to the useQuery that will be used to do the useQuery functionality. It will be used as import { useQuery } from '${hook.importPath}'. It allows both relative and absolute path. the path will be applied as is, so relative path should be based on the file being generated.

Type:string
Required:false
Default:'@tanstack/solid-query'

mutation

Override some useMutation behaviors.
To disable queries pass false.

Type:Mutation
Required:false
Query
typescript
type Mutation = {
  methods: Array<HttpMethod>
  importPath?: string
} | false

mutation.methods

Define which HttpMethods can be used for mutations

Type:Array<HttpMethod>
Required:false
Default:['post', 'put', 'delete']

mutation.importPath

Path to the useQuery that will be used to do the useQuery functionality. It will be used as import { useMutation } from '${hook.importPath}'. It allows both relative and absolute path. the path will be applied as is, so relative path should be based on the file being generated.

Type:string
Required:false
Default:'@tanstack/solid-query'

mutationKey

Customize the mutationKey.

WARNING

When using a string you need to use JSON.stringify.

Type:(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]
Required:false

include

Array containing include parameters to include tags, operations, methods, paths, or content types.

Type:Array<Include>
Required:false
Include
typescript
export type Include = {
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
  pattern: string | RegExp
}

exclude

Array containing exclude parameters to exclude or skip tags, operations, methods, paths, or content types.

Type:Array<Exclude>
Required:false
Exclude
typescript
export type Exclude = {
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
  pattern: string | RegExp
}

override

Array containing override parameters to override options based on tags, operations, methods, paths, or content types.

Type:Array<Override>
Required:false
Override
typescript
export type Override = {
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
  pattern: string | RegExp
  options: PluginOptions
}

generators

See Generators for more information on how to use generators.

Type:Array<Generator<PluginSolidQuery>>
Required:false

transformers

transformers.name

Customize the names based on the type that is provided by the plugin.

Type:(name: string, type?: ResolveType) => string
Required:false
typescript
type ResolveType = 'file' | 'function' | 'type' | 'const'

Example

typescript
import { 
defineConfig
} from '@kubb/core'
import {
pluginOas
} from '@kubb/plugin-oas'
import {
pluginSolidQuery
} from '@kubb/plugin-solid-query'
import {
pluginTs
} from '@kubb/plugin-ts'
export default
defineConfig
({
input
: {
path
: './petStore.yaml',
},
output
: {
path
: './src/gen',
},
plugins
: [
pluginOas
(),
pluginTs
(),
pluginSolidQuery
({
output
: {
path
: './hooks',
},
group
: {
type
: 'tag',
name
: ({
group
}) => `${
group
}Hooks`,
},
client
: {
dataReturnType
: 'full',
},
query
: {
methods
: [ 'get' ],
importPath
: "@tanstack/solid-query"
}, }), ], })

See Also

Released under the MIT License.