Introduction

Eden-Query is a type-safe, API client for Elysia.js that provides asynchronous state management with Tanstack Query.

The Eden-Query project is a collection of libraries that includes the RPC client, @ap0nia/eden, and framework-specific integrations, such as @ap0nia/eden-react-query and @ap0nia/eden-svelte-query for React and Svelte respectively.

NOTE

The @ap0nia/eden library is a distinct, but functionally identical implementation of the official @elysiajs/eden library.

Why

Eden-Query is most valuable to frontend projects that connect to an Elysia.js backend. The Elysia.js types may be exposed from a separate project within a monorepo, or within a single project that leverages a full-stack solution such as Next.js or SvelteKit.

See the examples directory for examples.

Features

  • 🌎 Framework agnostic.
  • 🦺 Full end-to-end type-safety.
  • ✅ Fully supports REST API standards.
  • 🖥️SSR support and examples.
  • ✨ Reactive and infinite queries.
  • ⚡ Batching requests on both the client and server.
  • 🔗 Links for customizing the flow of data.
  • 👀 Data transformers for enhanced JSON handling.

Core Features

Implementations

Eden-Query offers two implementations, fetch and treaty, just like the official eden library.

treaty
fetch
eden.greeting({ name: 'Elysia' }).get.createQuery()

Fetch (WIP)

  • Tanstack-query hooks are exposed at the root of the proxy.
  • The full path to the route is provided as the first argument.
  • Input to the route, such as query and route parameters, are provided after the path.
WARNING

This has not been implemented for any framework yet...

Treaty

  • API routes are split by their path segments, and represented as a nested object.
  • /api/a/b -> eden.api.a.b.
  • The method and hook are provided as the last two property accesses.
  • eden.api.a.b.get.createQuery -> createQuery for GET request to /api/a/b.

Example Application

server.ts
import { Elysia, t } from 'elysia' const app = new Elysia() .get('/nendoroid/:id/name', () => { return 'Skadi' }) .put( '/nendoroid/:id', (context) => { return { status: 'OK', received: context.body } }, { body: t.Object({ name: t.String(), from: t.String(), }), }, ) .listen(3000) export type App = typeof app

Example React-Query Usage

A React client application cna use the hooks from Eden-Query to manage asynchronous state from the Elysia server application.

index.ts
import { createEdenTreatyReactQuery } from '@ap0nia/eden-react-query' import type { App } from './server' /** * The domain is usually needed if the client application * is not part of a full stack framework. * * For example, a React single-page-application would need to specify * the server domain, while a Next.js application would not. * * Follow the steps provided in the specific framework integration. */ const domain = 'localhost:3000' export const app = createEdenTreatyReactQuery<App>({ domain }) // useQuery for [GET] request to '/nendoroid/:id/name' const { data } = await app.nendoroid({ id: 'skadi' }).name.get.useQuery() // useMutation for [PUT] request to '/nendoroid/:id' const { data: nendoroid, error, mutateAsync } = app.nendoroid({ id: 1895 }).put.useMutation() // Peform the mutation... mutateAsync({ name: 'Skadi', from: 'Arknights' }).then((result) => { result })
TIP

useMutation does not actually perform the request. The result of useMutation has mutate and mutateAsync methods that receive the input to make the request.

Read more about mutations here

Comparison with tRPC

This library supports the same type of links that tRPC has.

The official eden library only resolves requests, so Eden-Query provides helper methods to quickly initialize a client that does the same.

Read more about Eden-Query links here.

eden.createHttpClient

This creates a basic eden client that resolves requests in the same way as the official eden implementation.

import { createEdenTreatyReactQuery, httpLink } from '@ap0nia/eden-react-query' const eden = createEdenTreatyReactQuery() const domain = 'http://localhost:3000' // Both are the exact same clients. const clientOne = eden.createClient({ links: [httpLink({ domain })], }) const clientTwo = eden.createHttpClient({ domain })
TIP

Using this helper method means you don't have to initialize an httpLink from scratch, and you can provide HTTPLinkOptionsdirectly to the method to create the client.

eden.createHttpBatchClient

Creates a client that can combine multiple requests into a single batch request.

WARNING

In order for this client to work properly, the Elysia server application must use the batchPlugin or edenPlugin with the batchproperty defined.

Elysia.js application with batching enabled

server.ts
import { Elysia, t } from 'elysia' import { edenPlugin } from '@ap0nia/eden-react-query/server' const app = new Elysia().use(edenPlugin({ batch: true })) export type App = typeof app

React-Query Batch Client

index.ts
import { createEdenTreatyReactQuery, httpBatchLink } from '@ap0nia/eden-react-query' import type { App } from './server' const eden = createEdenTreatyReactQuery<App>() const domain = 'http://localhost:3000' // Both are the exact same clients. const clientOne = eden.createClient({ links: [httpBatchLink({ domain, endpoint: '/api/batch' })], }) const clientTwo = eden.createHttpBatchClient({ domain })

Transformers

This library supports the same transformer API as tRPC.

INFO

The transformers will only modify request.body. So this will NOTaffect GET, OPTIONS, or HEAD requests; only POST, PUT, PATCH, etc.

Read more about this Eden-Query transformers here.

tRPC

tRPC is a type-safe, full-stack integration that allows developers to easily types between their frontend and backend without code generation.

The core architecture and public API of Eden-Query was inspired by tRPC, making transitions between the two libraries feel seamless.

Some documentation may link to this project to provide additional information and context.