useQuery Hook
React hook for fetching data from API routes
Overview
The useQuery hook provides a React hook for fetching data from your API routes. It's automatically generated for each API route handler.
Basic Usage
import { api } from "@/.bunbox/api-client";
export default function UsersPage() {
const { data, loading, error, refetch } = api.users.listUsers.useQuery();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{JSON.stringify(data)}</div>;
}Hook Return Values
{
data: TResponse | undefined; // Response data
loading: boolean; // Loading state
error: Error | undefined; // Error if request failed
refetch: () => Promise<void>; // Manually refetch
}Query Parameters
Query parameters are flattened into the options object:
const { data } = api.users.listUsers.useQuery({
role: "admin", // Directly in options, not nested
});Route Parameters
Dynamic route parameters are also flattened:
// For route: /api/users/[id]/route.ts
const { data } = api.users.getUser.useQuery({
id: "123", // Flattened, not { params: { id } }
});Request Body
For POST/PUT/PATCH handlers, body fields are flattened:
const { data } = api.users.createUser.useQuery({
name: "Alice",
email: "[email protected]",
});Conditional Fetching
Disable automatic fetching:
const { data, refetch } = api.users.listUsers.useQuery({
enabled: false, // Don't fetch automatically
});
// Manually trigger fetch
<button onClick={refetch}>Load Users</button>;Refetching
Manually refetch data:
const { data, refetch } = api.users.listUsers.useQuery();
<button onClick={() => refetch()}>Refresh</button>;Custom Headers
Pass custom headers:
const { data } = api.users.listUsers.useQuery({
headers: {
Authorization: "Bearer token",
},
});Complete Example
import { useState } from "react";
import { api } from "@/.bunbox/api-client";
export default function UsersPage() {
const [roleFilter, setRoleFilter] = useState<"admin" | "user" | "">("");
// Query params are flattened - just pass role directly
const { data, loading, error, refetch } = api.users.listUsers.useQuery({
...(roleFilter ? { role: roleFilter } : {}),
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<select
value={roleFilter}
onChange={(e) => setRoleFilter(e.target.value as any)}
>
<option value="">All</option>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>
<button onClick={() => refetch()}>Refresh</button>
<ul>
{data?.users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}Caching
The hook automatically caches responses based on the request parameters. Subsequent calls with the same parameters return cached data.
Error Handling
Handle errors:
const { data, error } = api.users.listUsers.useQuery();
if (error) {
return <div>Failed to load: {error.message}</div>;
}Loading States
Track loading state:
const { data, loading } = api.users.listUsers.useQuery();
return (
<div>
{loading && <div>Loading...</div>}
{data && <div>{JSON.stringify(data)}</div>}
</div>
);Combining Parameters
For routes with multiple parameter types, everything is flattened:
// Route: /api/users/[id]/route.ts with query params
const { data } = api.users.getUser.useQuery({
id: "123", // Route param - goes to URL
expand: "posts", // Query param - goes to URL query string
enabled: true, // Hook option - controls fetching
});