createEdenTanStackQuery
Main factory function for creating typed Eden TanStack Query utilities
createEdenTanStackQuery
The main factory function that creates typed providers and hooks for using Eden with TanStack Query.
Signature
function createEdenTanStackQuery<TApp extends AnyElysia>(): CreateEdenTanStackQueryResult<TApp>Type Parameters
| Parameter | Description |
|---|---|
TApp | Your Elysia application type, extends AnyElysia |
Returns
Returns an object containing:
| Property | Type | Description |
|---|---|---|
EdenProvider | React.FC<EdenProviderProps<TApp>> | Provider component for wrapping your app |
useEden | () => EdenOptionsProxy<TApp> | Hook to get the typed options proxy |
useEdenClient | () => Treaty.Create<TApp> | Hook to get the raw Eden client |
Usage
Basic Setup
import { createEdenTanStackQuery } from 'eden-tanstack-react-query'
import { treaty } from '@elysiajs/eden'
import type { App } from './server'
// Create typed hooks and provider
export const { EdenProvider, useEden, useEdenClient } = createEdenTanStackQuery<App>()
// Create the Eden client
export const edenClient = treaty<App>('http://localhost:3000')Provider Setup
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { EdenProvider, edenClient } from './lib/eden'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<EdenProvider client={edenClient}>
<YourApp />
</EdenProvider>
</QueryClientProvider>
)
}Using in Components
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useEden } from './lib/eden'
function UserList() {
const eden = useEden()
const queryClient = useQueryClient()
// Fully typed query
const { data: users } = useQuery(eden.users.get.queryOptions())
// Fully typed mutation
const createUser = useMutation({
...eden.users.post.mutationOptions(),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: eden.users.get.queryKey() })
}
})
return (
<div>
<ul>
{users?.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
<button onClick={() => createUser.mutate({ name: 'New User' })}>
Add User
</button>
</div>
)
}Types
CreateEdenTanStackQueryResult
| Property | Type | Description |
|---|---|---|
EdenProvider | React.FC<EdenProviderProps> | Provider component that supplies Eden client to children |
useEden | () => EdenOptionsProxy<TApp> | Hook returning the options proxy with decorated routes |
useEdenClient | () => Treaty.Create<TApp> | Hook returning the raw Eden Treaty client |
EdenProviderProps
| Property | Type | Description |
|---|---|---|
client | Treaty.Create<TApp> | Eden Treaty client instance |
queryClient | QueryClient | Optional, deprecated, and unused. Pass it to QueryClientProvider instead. |
children | React.ReactNode | React children |
How It Works
-
Type Extraction: The factory uses TypeScript's type inference to extract route definitions from your Elysia app type.
-
Provider Creation: Creates a React context provider that holds the Eden client and its options proxy.
-
Proxy Generation: When
useEden()is called, it creates a proxy object that mirrors your API structure but returns TanStack Query options instead of making API calls directly. -
Method Decoration: Each route is decorated with appropriate methods:
- GET, HEAD, OPTIONS routes get
queryOptions,queryKey,queryFilter, and infinite query variants - POST, PUT, PATCH, DELETE routes get
mutationOptionsandmutationKey
- GET, HEAD, OPTIONS routes get
Multiple API Instances
Each factory call creates separate Eden contexts, but all instances use the same query key format. Keys do not include the backend URL or Eden context. If two backends expose the same route with the same input, their queries collide in a shared QueryClient.
Use a separate QueryClient for each backend. This example puts each API in a sibling provider scope. Both backends expose GET /users returning an array of users:
import { treaty } from '@elysiajs/eden'
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
import { createEdenTanStackQuery } from 'eden-tanstack-react-query'
import { useState } from 'react'
import type { InternalApp } from './internal-server'
import type { ExternalApp } from './external-server'
const internalApi = createEdenTanStackQuery<InternalApp>()
const externalApi = createEdenTanStackQuery<ExternalApp>()
const internalClient = treaty<InternalApp>('http://localhost:3000')
const externalClient = treaty<ExternalApp>('http://localhost:4000')
function App() {
const [internalQueryClient] = useState(() => new QueryClient())
const [externalQueryClient] = useState(() => new QueryClient())
return (
<>
<QueryClientProvider client={internalQueryClient}>
<internalApi.EdenProvider client={internalClient}>
<InternalUsers />
</internalApi.EdenProvider>
</QueryClientProvider>
<QueryClientProvider client={externalQueryClient}>
<externalApi.EdenProvider client={externalClient}>
<ExternalUsers />
</externalApi.EdenProvider>
</QueryClientProvider>
</>
)
}
function InternalUsers() {
const eden = internalApi.useEden()
const { data } = useQuery(eden.users.get.queryOptions())
return <p>Internal users: {data?.length}</p>
}
function ExternalUsers() {
const eden = externalApi.useEden()
const { data } = useQuery(eden.users.get.queryOptions())
return <p>External users: {data?.length}</p>
}useQuery and useQueryClient use the nearest QueryClientProvider. Nesting Eden providers does not select a cache. If one component queries both APIs, pass the corresponding QueryClient explicitly as the second argument to each useQuery(options, queryClient) call, and use that same client for invalidation or prefetching.
See Also
- useEden - The main hook for accessing query options
- EdenProvider - Provider component details