React Query v5 Hooks
Invokix is the only tool that generates React Query v5 hooks from your API contract. Every endpoint becomes a production-ready, fully typed hook — with proper query keys, error handling, and cache invalidation built in.
What gets generated
For every GET endpoint, Invokix generates a useQuery hook. For every POST, PUT, PATCH, and DELETE endpoint, it generates a useMutation hook. Every hook is typed against the schemas in your contract.
// Auto-generated by Invokix — do not edit manually
// Contract: Orders API v1.2 — published March 5, 2025
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import type { Order, CreateOrderBody } from "@/types/orders"
export const orderKeys = {
all: ["orders"] as const,
list: () => [...orderKeys.all, "list"] as const,
detail: (id: string) => [...orderKeys.all, "detail", id] as const,
}
export function useGetOrders() {
return useQuery({
queryKey: orderKeys.list(),
queryFn: async (): Promise<Order[]> => {
const res = await fetch("/api/orders")
if (!res.ok) throw new Error("Failed to fetch orders")
return res.json()
},
})
}
export function useGetOrder(orderId: string) {
return useQuery({
queryKey: orderKeys.detail(orderId),
queryFn: async (): Promise<Order> => {
const res = await fetch("/api/orders/" + orderId)
if (!res.ok) throw new Error("Failed to fetch order")
return res.json()
},
enabled: !!orderId,
})
}
export function useCreateOrder() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (body: CreateOrderBody): Promise<Order> => {
const res = await fetch("/api/orders", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
})
if (!res.ok) throw new Error("Failed to create order")
return res.json()
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: orderKeys.list() })
},
})
}
export function useCancelOrder() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (orderId: string): Promise<void> => {
const res = await fetch("/api/orders/" + orderId, { method: "DELETE" })
if (!res.ok) throw new Error("Failed to cancel order")
},
onSuccess: (_data, orderId) => {
queryClient.invalidateQueries({ queryKey: orderKeys.list() })
queryClient.removeQueries({ queryKey: orderKeys.detail(orderId) })
},
})
}Using the generated hooks
Drop the hooks directly into your components. Every hook is fully typed — no manual type annotations needed.
import { useGetOrders, useCreateOrder, useCancelOrder } from "@/hooks/useOrders"
import type { CreateOrderBody } from "@/types/orders"
export function OrderList() {
const { data: orders, isLoading, error } = useGetOrders()
const createOrder = useCreateOrder()
const cancelOrder = useCancelOrder()
if (isLoading) return <Spinner />
if (error) return <ErrorMessage />
return orders?.map((order) => (
<OrderCard
key={order.id}
order={order}
onCancel={() => cancelOrder.mutate(order.id)}
/>
))
}Query key structure
Every generated hook uses a structured query key factory — orderKeys.all, orderKeys.list(), orderKeys.detail(id). This means cache invalidation always works correctly. When you create or update an order, the list cache invalidates automatically. When you cancel an order, both the list and the detail cache for that specific order are cleared.
You never need to manage query keys manually.
Requirements
React Query v5 must be installed in your project:
pnpm add @tanstack/react-queryWrap your app in a QueryClientProvider:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
const queryClient = new QueryClient()
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</body>
</html>
)
}