Skip to main content
This example demonstrates integration with TanStack Query (React Query) for powerful data fetching, caching, and state management in React applications.

Configuration

OpenAPI TypeScript Config

Create openapi-ts.config.ts in your project root:
openapi-ts.config.ts
import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input:
    'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml',
  logs: {
    path: './logs',
  },
  output: {
    path: './src/client',
    postProcess: ['oxfmt', 'eslint'],
  },
  plugins: [
    '@hey-api/client-fetch',
    '@hey-api/schemas',
    {
      instance: true,
      name: '@hey-api/sdk',
    },
    {
      enums: 'javascript',
      name: '@hey-api/typescript',
    },
    '@tanstack/react-query',
  ],
});
Key features:
  • @tanstack/react-query plugin generates hooks and options
  • @hey-api/client-fetch for Fetch API client
  • instance: true creates SDK instance

Package Dependencies

package.json
{
  "dependencies": {
    "@tanstack/react-query": "^5.0.0",
    "@tanstack/react-query-devtools": "^5.0.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@hey-api/openapi-ts": "latest",
    "typescript": "^5.9.0",
    "vite": "^7.0.0"
  },
  "scripts": {
    "openapi-ts": "openapi-ts",
    "dev": "vite"
  }
}

Application Setup

Main Entry Point

Configure the QueryClient and set up the global client:
src/main.tsx
import '@radix-ui/themes/styles.css';
import { Theme } from '@radix-ui/themes';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from 'react';
import ReactDOM from 'react-dom/client';

import App from './App.tsx';
import { client } from './client/client.gen';
import { Sdk } from './client/sdk.gen.ts';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60000,
    },
  },
});

// configure internal service client
client.setConfig({
  // set default base url for requests
  baseUrl: 'https://petstore3.swagger.io/api/v3',
  // set default headers for requests
  headers: {
    Authorization: 'Bearer <token_from_service_client>',
  },
});

new Sdk();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <Theme appearance="dark">
        <App />
      </Theme>
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  </React.StrictMode>,
);

Usage

Component with Queries and Mutations

src/App.tsx
import { useMutation, useQuery } from '@tanstack/react-query';
import { useEffect, useState } from 'react';

import {
  addPetMutation,
  getPetByIdOptions,
  updatePetMutation,
} from './client/@tanstack/react-query.gen';
import { createClient } from './client/client';
import { PetSchema } from './client/schemas.gen';
import type { Pet } from './client/types.gen';

const localClient = createClient({
  baseUrl: 'https://petstore3.swagger.io/api/v3',
  headers: {
    Authorization: 'Bearer <token_from_local_client>',
  },
});

localClient.interceptors.request.use((request, options) => {
  // Add authorization tokens to protected paths
  if (options.url === '/pet/{petId}' && options.method === 'GET' && Math.random() < 0.5) {
    request.headers.set('Authorization', 'Bearer <token_from_interceptor>');
  }
  return request;
});

function App() {
  const [pet, setPet] = useState<Pet>();
  const [petId, setPetId] = useState<number>();
  const [isRequiredNameError, setIsRequiredNameError] = useState(false);

  // Query with options
  const { data, error } = useQuery({
    ...getPetByIdOptions({
      client: localClient,
      path: {
        petId: petId!,
      },
    }),
    enabled: Boolean(petId),
  });

  // Add pet mutation
  const addPet = useMutation({
    ...addPetMutation(),
    onError: (error) => {
      console.log(error);
      setIsRequiredNameError(false);
    },
    onSuccess: (data) => {
      setPet(data);
      setIsRequiredNameError(false);
    },
  });

  // Update pet mutation
  const updatePet = useMutation({
    ...updatePetMutation(),
    onError: (error) => {
      console.log(error);
    },
    onSuccess: (data) => {
      setPet(data);
    },
  });

  const onAddPet = async (formData: FormData) => {
    // Form validation using schemas
    if (PetSchema.required.includes('name') && !formData.get('name')) {
      setIsRequiredNameError(true);
      return;
    }

    addPet.mutate({
      body: {
        category: {
          id: 0,
          name: formData.get('category') as string,
        },
        id: 0,
        name: formData.get('name') as string,
        photoUrls: ['string'],
        status: 'available',
        tags: [{ id: 0, name: 'string' }],
      },
    });
  };

  const onGetPetById = async () => {
    setPetId(Math.floor(Math.random() * 10 + 1));
  };

  const onUpdatePet = async () => {
    updatePet.mutate({
      body: {
        category: { id: 0, name: 'Cats' },
        id: 2,
        name: 'Updated Kitty',
        photoUrls: ['string'],
        status: 'available',
        tags: [{ id: 0, name: 'string' }],
      },
      headers: {
        Authorization: 'Bearer <token_from_method>',
      },
    });
  };

  useEffect(() => {
    if (error) {
      console.log(error);
      return;
    }
    setPet(data!);
  }, [data, error]);

  return (
    <div>
      <h1>@hey-api/openapi-ts 🤝 TanStack React Query</h1>
      
      {pet && (
        <div>
          <h2>{pet.name}</h2>
          <p>Category: {pet.category?.name ?? 'N/A'}</p>
        </div>
      )}
      
      <button onClick={onGetPetById}>Get Random Pet</button>
      <button onClick={onUpdatePet}>Update Pet</button>
      
      <form onSubmit={(e) => {
        e.preventDefault();
        onAddPet(new FormData(e.currentTarget));
      }}>
        <input name="name" placeholder="Name" />
        <input name="category" placeholder="Category" />
        <button type="submit">Add Pet</button>
      </form>
    </div>
  );
}

export default App;

Generated Helpers

The @tanstack/react-query plugin generates several helpers:

Query Options

// Generated query options
const options = getPetByIdOptions({
  path: { petId: 123 },
});

// Use with useQuery
const { data } = useQuery(options);

// Use with useQueries for parallel queries
const results = useQueries({
  queries: [
    getPetByIdOptions({ path: { petId: 1 } }),
    getPetByIdOptions({ path: { petId: 2 } }),
  ],
});

Mutation Helpers

// Generated mutation options
const mutation = useMutation({
  ...addPetMutation(),
  onSuccess: (data) => {
    console.log('Pet added:', data);
  },
});

// Call the mutation
mutation.mutate({
  body: {
    name: 'Fluffy',
    status: 'available',
    photoUrls: [],
  },
});

Key Features

Type Safety

All queries and mutations are fully typed:
// Query result is typed as Pet | undefined
const { data } = useQuery(getPetByIdOptions({ path: { petId: 1 } }));

// Mutation payload is validated
addPet.mutate({
  body: {
    name: 'required',
    photoUrls: ['required'],
    // TypeScript error if required fields missing
  },
});

Schema Validation

Use generated schemas for runtime validation:
import { PetSchema } from './client/schemas.gen';

// Check required fields
if (PetSchema.required.includes('name')) {
  // Name is required
}

// Access schema properties
console.log(PetSchema.properties.name.type); // 'string'

Client Interceptors

Add middleware for authentication and logging:
localClient.interceptors.request.use((request, options) => {
  // Add auth header
  request.headers.set('Authorization', `Bearer ${getToken()}`);
  return request;
});

localClient.interceptors.response.use((response) => {
  // Log responses
  console.log('Response:', response.status);
  return response;
});

Running the Example

1

Clone the repository

git clone https://github.com/hey-api/openapi-ts.git
cd openapi-ts
2

Install dependencies

pnpm install
3

Run the React Query example

pnpm example react-query dev
4

Open in browser

Navigate to http://localhost:5173

Full Example

View the complete example in the repository: React Query Example on GitHub

Learn More

TanStack Query Plugin

Client Configuration

SDK Plugin

Examples Overview

Build docs developers (and LLMs) love