TanStack Query (FKA React Query) integration
TSDL natively integrates with React Query.
Installation
Ensure you have a server
If you have a server, great! You're ready to use TSDL + React Query. If not, head over to server-side setup and come back here.
Install dependencies (front-end)
pnpm add @tsdl/react-query @tanstack/react-query
Create a client
Follow the steps in client-side setup and under "Create client" select "React Query".
Usage
Basic usage
import { tsdl } from "./tsdl";
export default function App() {
const applesQuery = tsdl.fruits.fetchApples.useQuery();
}
useQuery options (without TSDL input)
If the query has no inputs, the first argument will be useQuery
options
import { tsdl } from "./tsdl";
export default function App() {
const applesQuery = tsdl.fruits.fetchApples.useQuery({
enabled: false,
intialData: ["Golden Delicious", "Cortland"],
});
}
useQuery options (with TSDL input)
If the TSDL query has an input, the first argument is input and the second argument is useQuery
options.
import { tsdl } from "./tsdl";
export default function App() {
const applesQuery = tsdl.fruits.fetchFruit.useQuery("apples", {
enabled: false,
intialData: ["Golden Delicious", "Cortland"],
});
}
Pass arguments to useQuery
export default function App() {
const cars = tsdl.cars.fetchCars.useQuery({
brand: "volvo",
});
}
const router = tsdl.router({
cars: tsdl.router({
fetchCars: tsdl
.input(
z.object({
brand: z.string(),
})
)
.query(async ({ input }) => {
return await db.cars.findMany({ where: input });
}),
}),
});
Mutations
React Query's useMutation
is fully supported.
For more information about useMutation
, refer to the
official documentation (opens in a new tab).
export default function App() {
const addCar = tsdl.cars.addCar.useMutation({
/* options? */
});
const [name, setName] = useState("");
return (
<form
onSubmit={(e) => {
e.preventDefault();
addCar.mutate(name, {
onSuccess() {
tsdl.cars.invalidate();
},
});
}}
>
<input
name="name"
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
<button disabled={addCar.isLoading} type="submit">
Add!
</button>
</form>
);
}
Invalidation
This is a shorthand for queryClient.invalidateQueries
(opens in a new tab).
.invalidate()
is exposed for all nodes of the router, not just the leaf nodes.
tsdl.cars.brands.invalidate();
tsdl.cars.invalidate();
is equivelant to
query.invalidateQueries({
queryKey: ["cars", "brands"],
});
query.invalidateQueries({
queryKey: ["cars"],
});
Both the optional filter
and options
arguments are supported. Note that queryKey
is omitted in filters
.
Inferernce
It's often useful to access the return type of a query however using Awaited<ReturnType<typeof tsdl.cars.brands>>
is a bit cumbersome.
Therefore, TSDL exposes .infer
for all leaf nodes (queries).
Server component example:
async function Sidebar() {
const user = await tsdl.user.me();
return <UserItems initialData={user} />;
}
function UserItems({
initialData,
}: {
initialData: typeof tsdl.user.me.infer;
}) {
const userQuery = tsdl.user.useQuery({
initialData,
}); // no "logged out" state while loading! But also no reload required when user logs out :)
return <>...</>;
}