Eden TanStack Query
API Reference

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

ParameterDescription
TAppYour Elysia application type, extends AnyElysia

Returns

Returns an object containing:

PropertyTypeDescription
EdenProviderReact.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

lib/eden.ts
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

App.tsx
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

UserList.tsx
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

PropertyTypeDescription
EdenProviderReact.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

PropertyTypeDescription
clientTreaty.Create<TApp>Eden Treaty client instance
queryClientQueryClientOptional, deprecated, and unused. Pass it to QueryClientProvider instead.
childrenReact.ReactNodeReact children

How It Works

  1. Type Extraction: The factory uses TypeScript's type inference to extract route definitions from your Elysia app type.

  2. Provider Creation: Creates a React context provider that holds the Eden client and its options proxy.

  3. 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.

  4. 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 mutationOptions and mutationKey

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

On this page