Type-safe TanStack Query integration for Eden Treaty (Elysia)
Using Eden Treaty with TanStack Query manually requires repetitive boilerplate:
// Without treaty-tanstack-query π«
const fetchTasks = async (filters: TaskFilters) => {
const response = await client.api.tasks.get({ query: filters });
if (response.error) throw response.error;
return response.data;
};
const { data } = useQuery({
queryKey: ['api', 'tasks', filters], // Easy to typo!
queryFn: () => fetchTasks(filters),
});
// Mutation with manual invalidation
const createTask = useMutation({
mutationFn: async (input: CreateTaskInput) => {
const response = await client.api.tasks.post(input);
if (response.error) throw response.error;
return response.data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['api', 'tasks'] }); // Hope you remembered the key!
},
});With this library, it becomes:
// With treaty-tanstack-query π
const treaty = useTreaty();
const queryClient = useQueryClient();
const { data } = useQuery(treaty.api.tasks.queryOptions({ query: filters }));
const createTask = useMutation(
treaty.api.tasks.mutationOptions({
onSuccess: () => queryClient.invalidateQueries(treaty.api.tasks.pathFilter()),
})
);- Full type safety β Automatic inference from Elysia routes to
queryOptionsandmutationOptions - First-class TanStack Query v5 β Returns standard options objects that work with
useQuery,useMutation,useSuspenseQuery, etc. - Smart query keys β Hierarchical keys auto-generated from your API paths. Invalidate by path with
pathFilter() - Infinite queries β Automatic support when your input contains a
cursorfield - Real-time β WebSocket subscriptions via
subscriptionOptions()+useSubscription() - SSR & Suspense β Full support for React Suspense, streaming SSR, and hydration
npm install treaty-tanstack-react-query @tanstack/react-query @elysiajs/edenOther package managers
pnpm add treaty-tanstack-react-query @tanstack/react-query @elysiajs/edenbun add treaty-tanstack-react-query @tanstack/react-query @elysiajs/edenimport { treaty } from "@elysiajs/eden";
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
import { createTreatyContext } from "treaty-tanstack-react-query";
import type { App } from "./server"; // Your Elysia app type
// 1. Create the Eden client
const client = treaty<App>("http://localhost:3000");
// 2. Create the treaty context
const { TreatyProvider, useTreaty } = createTreatyContext<typeof client>();
// 3. Setup providers
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<TreatyProvider client={client} queryClient={queryClient}>
<Tasks />
</TreatyProvider>
</QueryClientProvider>
);
}
// 4. Use in components with full type safety!
function Tasks() {
const treaty = useTreaty();
// β
Fully typed - IDE autocomplete shows available endpoints
const { data: tasks } = useQuery(
treaty.api.tasks.queryOptions({ query: { status: "todo" } })
);
return (
<ul>
{tasks?.map(task => <li key={task.id}>{task.title}</li>)}
</ul>
);
}const { data } = useQuery(
treaty.api.tasks.queryOptions({
query: { status: "in-progress", priority: "high" }
})
);const treaty = useTreaty();
const queryClient = useQueryClient();
const updateTask = useMutation(
treaty.api.tasks({ id: taskId }).mutationOptions('patch', {
onSuccess: () => {
// Invalidate all task queries
queryClient.invalidateQueries(treaty.api.tasks.pathFilter());
},
})
);
updateTask.mutate({ status: "done" });// Automatically detected when your endpoint has a `cursor` parameter
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
treaty.api.tasks.infinite.infiniteQueryOptions({
query: { limit: 10 }
})
);const treaty = useTreaty();
const queryClient = useQueryClient();
const { lastMessage } = useSubscription(
treaty.ws.tasks.subscriptionOptions()
);
useEffect(() => {
if (lastMessage?.type === "task-updated") {
queryClient.invalidateQueries(treaty.api.tasks.pathFilter());
}
}, [lastMessage]);Full documentation is available at abanoubnassem.github.io/treaty-tanstack-query
- Getting Started
- Query Options
- Mutation Options
- Infinite Queries
- WebSocket Subscriptions
- SSR & Suspense
βββ packages/
β βββ treaty-tanstack-react-query/ # The library
βββ apps/
β βββ react-fullstack-example/ # Demo app (Task Manager)
βββ docs/ # VitePress documentation
# Install dependencies
bun install
# Run the example app
bun run dev
# Run tests
bun run test
# Build
bun run buildContributions are welcome! Please read our Contributing Guide before submitting a PR.
Built with β€οΈ for the Elysia + TanStack Query community