Skip to content

Templates tutorial

This tutorial will describe how you can setup Kubb + use the Swagger-client plugin to generate a client based on the petStore.yaml file with a defined template.

More info about how templates are working behind the scenes can be found here.


The setup will contain from the beginning the following folder structure:
typescript
.
├── src
├── templates
├── petStore.yaml
├── kubb.config.ts
└── package.json

Step one

Create a React component inside the templates folder that will be used to override the default behavior of the @kubb/swagger-client generated client.

TIP

Make sure you inherit from Client.templates.default instead of Client.templates.

In the future we will add more templates so that's why we have default.

typescript
import { Client, Operations } from '@kubb/swagger-client/components'

type Templates = {
  operations: typeof Operations.templates
  client: typeof Client.templates
}
export type Options = {
  // ....
  /**
   * Make it possible to override one of the templates
   */
  templates?: Partial<Templates>
}

The following component will use the props of the template React.ComponentProps<typeof Client.templates.default> and return based on those props a function axios.get.

Here we also need to add a new import and for that we use File.Import. For the other props, we just pass them(JSDoc, params, ...).

typescript
import { File, Function } from '@kubb/react'
import { Client } from '@kubb/swagger-client/components'
import React from 'react'

function ClientTemplate({ name, generics, returnType, params, JSDoc, client }: React.ComponentProps<typeof Client.templates.default>) {
  const clientParams = [client.path.template, client.withData ? 'data' : undefined, 'options'].filter(Boolean).join(', ')

  return (
    <>
      <File.Import name="axios" path="axios" />
      <Function name={name} async export generics={generics} returnType={returnType} params={params} JSDoc={JSDoc}>
        {`return axios.${client.method}(${clientParams}`}
      </Function>
    </>
  )
}

Step two

Based on the type we know that we need to return a template object with client and/or operations(see types.ts). To make it possible to override the templates we need to add the following export.

TIP

Don't forget the default, in the future we will have multiple variants but the default will be used as a fallback.

typescript
import { PluginOptions } from '@kubb/swagger-client'

export const templates: PluginOptions['options']['templates'] = {
  client: {
    default: ClientTemplate,
  },
}

This will result in the following folder structure.

typescript
.
├── src/
├── templates/
│   └── CustomClientTemplates.tsx
├── petStore.yaml
├── kubb.config.ts
└── package.json

Step three

Update your kubb.config.ts file to include the templates options.

typescript
import { defineConfig } from '@kubb/core'
import { definePlugin as createSwagger } from '@kubb/swagger'
import { definePlugin as createSwaggerClient } from '@kubb/swagger-client'

import { templates } from './templates/CustomClientTemplate.tsx'

export default defineConfig(async () => {
  return {
    root: '.',
    input: {
      path: './petStore.yaml',
    },
    output: {
      path: './src',
    },
    plugins: [
      createSwagger(
        {
          output: false,
          validate: true,
        },
      ),
      createSwaggerClient(
        {
          output: {
            path: 'models',
          },
          templates,
        },
      ),
    ],
  }
})

Step four

Run the Kubb script with the following command.

shell
bun run generate
shell
pnpm run generate
shell
npm run generate
shell
yarn run generate

Step five

End result of a custom template.

typescript
import { File, Function } from '@kubb/react'
import { PluginOptions } from '@kubb/swagger-client'
import { Client } from '@kubb/swagger-client/components'
import React from 'react'

function ClientTemplate({ name, generics, returnType, params, JSDoc, client }: React.ComponentProps<typeof Client.templates.default>) {
  const clientParams = [client.path.template, client.withData ? 'data' : undefined, 'options'].filter(Boolean).join(', ')

  return (
    <>
      <File.Import name="axios" path="axios" />
      <Function name={name} async export generics={generics} returnType={returnType} params={params} JSDoc={JSDoc}>
        {`return axios.${client.method}(${clientParams}`}
      </Function>
    </>
  )
}

export const templates: PluginOptions['options']['templates'] = {
  client: {
    default: ClientTemplate,
  },
}
typescript
import client from '@kubb/swagger-client/client'
import type { ResponseConfig } from '@kubb/swagger-client/client'
import type { AddPetMutationRequest, AddPetMutationResponse } from '../../../models/ts/petController/AddPet'

/**
 * @description Add a new pet to the store
 * @summary Add a new pet to the store
 * @link /pet */
export async function addPet(
  data: AddPetMutationRequest,
  options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<AddPetMutationResponse>['data']> {
  const { data: resData } = await client<AddPetMutationResponse, AddPetMutationRequest>({
    method: 'post',
    url: `/pet`,
    data,
    ...options,
  })
  return resData
}
typescript
import client from '@kubb/swagger-client/client'
import axios from 'axios'
import type { ResponseConfig } from '@kubb/swagger-client/client'
import type { AddPetMutationRequest, AddPetMutationResponse } from '../../../models/ts/petController/AddPet'

/**
 * @description Add a new pet to the store
 * @summary Add a new pet to the store
 * @link /pet */
export async function addPet(
  data: AddPetMutationRequest,
  options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<AddPetMutationResponse>['data']> {
  return axios.post(`/pet`, data, options)
}

Released under the MIT License.