Skip to main content
Generate type-safe React Query (TanStack Query) hooks from your service definitions for seamless API integration in React applications.

Usage

apicentric simulator generate-query --file <INPUT> --output <OUTPUT>

Arguments

--file
string
required
Path to the Apicentric service definition YAML file to generate React Query hooks from.
--output
string
required
Path where the generated TypeScript/JavaScript file will be written. Should have a .ts or .tsx extension.

Options

--config
string
default:"apicentric.json"
Path to the configuration file (global option).
--mode
string
Execution mode override: ci, development, or debug (global option).
--dry-run
boolean
default:"false"
Show what would be generated without actually creating the output file (global option).
--verbose
boolean
default:"false"
Enable verbose output for detailed logging (global option).
--db-path
string
default:"apicentric.db"
Path to the SQLite database for simulator storage (global option).

Examples

Generate React Query hooks

apicentric simulator generate-query --file services/users.yaml --output src/hooks/useUsers.ts
Example output:
✅ Exported React Query hooks to src/hooks/useUsers.ts

Generate for multiple services

apicentric simulator generate-query --file services/products.yaml --output src/hooks/useProducts.ts
apicentric simulator generate-query --file services/orders.yaml --output src/hooks/useOrders.ts

Dry run mode

apicentric simulator generate-query --file services/api.yaml --output hooks.ts --dry-run
Example output:
🏃 Dry run: Would export React Query hooks from 'services/api.yaml' to 'hooks.ts'

Generated output

The command generates React Query hooks for:
  • useQuery hooks - For GET endpoints (data fetching)
  • useMutation hooks - For POST, PUT, PATCH, DELETE endpoints (data mutations)
  • TypeScript types - Request and response types
  • Query keys - Standardized cache keys for each endpoint
  • API client - Fetch-based functions for each endpoint

Example service definition

name: users
server:
  port: 8001
  base_path: /api/v1
types:
  User:
    type: object
    properties:
      id:
        type: string
      name:
        type: string
      email:
        type: string
  CreateUserRequest:
    type: object
    properties:
      name:
        type: string
      email:
        type: string
endpoints:
  - path: /users
    method: GET
    response:
      status: 200
      body:
        type: array
        items:
          $ref: "#/types/User"
  - path: /users/:id
    method: GET
    response:
      status: 200
      body:
        $ref: "#/types/User"
  - path: /users
    method: POST
    request:
      body:
        $ref: "#/types/CreateUserRequest"
    response:
      status: 201
      body:
        $ref: "#/types/User"
  - path: /users/:id
    method: DELETE
    response:
      status: 204

Generated React Query hooks

// Generated by Apicentric
// Do not edit manually

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// Types
export interface User {
  id: string;
  name: string;
  email: string;
}

export interface CreateUserRequest {
  name: string;
  email: string;
}

// API Base URL
const API_BASE_URL = 'http://localhost:8001/api/v1';

// Query Keys
export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters?: any) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
};

// API Functions
async function getUsers(): Promise<User[]> {
  const response = await fetch(`${API_BASE_URL}/users`);
  if (!response.ok) throw new Error('Failed to fetch users');
  return response.json();
}

async function getUserById(id: string): Promise<User> {
  const response = await fetch(`${API_BASE_URL}/users/${id}`);
  if (!response.ok) throw new Error('Failed to fetch user');
  return response.json();
}

async function createUser(data: CreateUserRequest): Promise<User> {
  const response = await fetch(`${API_BASE_URL}/users`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
  if (!response.ok) throw new Error('Failed to create user');
  return response.json();
}

async function deleteUser(id: string): Promise<void> {
  const response = await fetch(`${API_BASE_URL}/users/${id}`, {
    method: 'DELETE',
  });
  if (!response.ok) throw new Error('Failed to delete user');
}

// React Query Hooks
export function useUsers() {
  return useQuery({
    queryKey: userKeys.lists(),
    queryFn: getUsers,
  });
}

export function useUser(id: string) {
  return useQuery({
    queryKey: userKeys.detail(id),
    queryFn: () => getUserById(id),
    enabled: !!id,
  });
}

export function useCreateUser() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: userKeys.lists() });
    },
  });
}

export function useDeleteUser() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: deleteUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: userKeys.all });
    },
  });
}

Use cases

React application integration

# Generate hooks
apicentric simulator generate-query --file services/api.yaml --output src/hooks/useApi.ts
// components/UserList.tsx
import { useUsers, useCreateUser } from '../hooks/useApi';

export function UserList() {
  const { data: users, isLoading, error } = useUsers();
  const createUser = useCreateUser();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  const handleCreate = async () => {
    await createUser.mutateAsync({
      name: 'John Doe',
      email: '[email protected]',
    });
  };

  return (
    <div>
      <button onClick={handleCreate}>Create User</button>
      <ul>
        {users?.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

Next.js integration

# Generate hooks for Next.js app
apicentric simulator generate-query --file services/api.yaml --output src/lib/api-hooks.ts
// app/users/page.tsx
'use client';

import { useUsers } from '@/lib/api-hooks';

export default function UsersPage() {
  const { data, isLoading } = useUsers();
  
  return (
    <div>
      {isLoading ? 'Loading...' : data?.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

Optimistic updates

// Using generated hooks with optimistic updates
import { useUser, useUpdateUser } from './hooks/useApi';

function UserProfile({ userId }: { userId: string }) {
  const { data: user } = useUser(userId);
  const updateUser = useUpdateUser();

  const handleUpdate = (name: string) => {
    updateUser.mutate(
      { id: userId, name },
      {
        onMutate: async (newData) => {
          // Optimistic update
          await queryClient.cancelQueries({ queryKey: userKeys.detail(userId) });
          const previous = queryClient.getQueryData(userKeys.detail(userId));
          queryClient.setQueryData(userKeys.detail(userId), newData);
          return { previous };
        },
        onError: (err, newData, context) => {
          // Rollback on error
          queryClient.setQueryData(userKeys.detail(userId), context?.previous);
        },
      }
    );
  };

  return <div>{user?.name}</div>;
}

Automated generation in build

// package.json
{
  "scripts": {
    "generate:hooks": "apicentric simulator generate-query --file services/api.yaml --output src/hooks/api.ts",
    "prebuild": "npm run generate:hooks",
    "build": "vite build"
  }
}

React Query setup

To use the generated hooks, set up React Query in your app:
// App.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000, // 1 minute
      retry: 1,
    },
  },
});

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* Your app components */}
    </QueryClientProvider>
  );
}
Generated hooks automatically include cache invalidation, type safety, and follow React Query best practices. They work seamlessly with the simulator for local development and can be configured to point to production APIs.

Best practices

  1. Generate during build - Add to build scripts to keep hooks in sync
  2. Customize base URL - Update API_BASE_URL for different environments
  3. Add error handling - Extend generated hooks with custom error handling
  4. Use query keys - Leverage exported query keys for cache management
  5. TypeScript strict mode - Enable strict mode for maximum type safety

Configuration

Custom base URL

Modify the generated file to use environment variables:
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8001/api/v1';

Custom fetch options

Add authentication headers:
function fetchWithAuth(url: string, options?: RequestInit) {
  return fetch(url, {
    ...options,
    headers: {
      ...options?.headers,
      'Authorization': `Bearer ${getToken()}`,
    },
  });
}

Troubleshooting

Failed to generate hooks

❌ Failed to generate hooks: missing endpoint definitions
Solution: Ensure your service definition includes endpoint definitions:
endpoints:
  - path: /users
    method: GET
    # ...

TypeScript errors in generated code

Solution: Install React Query types:
npm install @tanstack/react-query
npm install -D @types/react

Hooks not updating cache

Solution: Ensure you’re using the QueryClientProvider and the generated query keys for cache invalidation.

Build docs developers (and LLMs) love