Eden Treaty Legacy
NOTE
This is a documentation for Eden Treaty 1 (edenTreaty).
For a new project, we recommend starting with Eden Treaty 2 (treaty) instead.
Eden Treaty is an object‑like representation of an Elysia server.
It provides accessor methods like a normal object with type information directly from the server, helping us move faster and ensuring nothing breaks.
Getting Started
To use Eden Treaty, first export your existing Elysia server type:
// server.ts
import { Elysia, t } from 'elysia'
const app = new Elysia()
.get('/', () => 'Hi Elysia')
.get('/id/:id', ({ params: { id } }) => id)
.post('/mirror', ({ body }) => body, {
body: t.Object({
id: t.Number(),
name: t.String()
})
})
.listen(3000)
export type App = typeof app
Then import the server type, and consume the Elysia API on the client:
// client.ts
import { edenTreaty } from '@elysiajs/eden'
import type { App } from './server'
const app = edenTreaty<App>('http://localhost:')
// response type: 'Hi Elysia'
const { data: pong, error } = app.get()
// response type: 1895
const { data: id, error } = app.id['1895'].get()
// response type: { id: 1895, name: 'Skadi' }
const { data: nendoroid, error } = app.mirror.post({
id: 1895,
name: 'Skadi'
})
TIP
Eden Treaty is fully type‑safe with auto‑completion support.
Anatomy
Eden Treaty will transform all existing paths to an object‑like representation, that can be described as:
EdenTreaty.<1>.<2>.n.<method>({ ... })
Path
Eden will transform / into . which can be called with a registered method, for example:
/path→.path/nested/path→.nested.path
Path parameters
Path parameters will be mapped automatically by their name in the URL.
/id/:id→.id.<anyThing>- e.g.,
.id.hi - e.g.,
.id['123']
- e.g.,
TIP
If a path doesn’t support path parameters, TypeScript will show an error.
Query
You can append queries to a path with $query:
app.get({
$query: {
name: 'Eden',
code: 'Gold'
}
})
Fetch
Eden Treaty is a fetch wrapper; you can pass any valid Fetch parameters using $fetch:
app.post({
$fetch: {
headers: {
'x-organization': 'MANTIS'
}
}
})
Error Handling
Eden Treaty will return a value of data and error as a result, both fully typed.
// response type: { id: 1895, name: 'Skadi' }
const { data: nendoroid, error } = app.mirror.post({
id: 1895,
name: 'Skadi'
})
if (error) {
switch (error.status) {
case 400:
case 401:
warnUser(error.value)
break
case 500:
case 502:
emergencyCallDev(error.value)
break
default:
reportError(error.value)
break
}
throw error
}
const { id, name } = nendoroid
Both data and error will be typed as nullable until you confirm their status with a type guard.
In simple terms: if the fetch is successful, data has a value and error is null, and vice‑versa.
TIP
Error is wrapped with anErrorand its value can be retrieved fromError.value.
Error type based on status
Both Eden Treaty and Eden Fetch can narrow down an error type based on status code if you explicitly provide an error type in the Elysia server.
// server.ts
import { Elysia, t } from 'elysia'
const app = new Elysia()
.model({
nendoroid: t.Object({
id: t.Number(),
name: t.String()
}),
error: t.Object({
message: t.String()
})
})
.get('/', () => 'Hi Elysia')
.get('/id/:id', ({ params: { id } }) => id)
.post('/mirror', ({ body }) => body, {
body: 'nendoroid',
response: {
200: 'nendoroid',
400: 'error',
401: 'error'
}
})
.listen(3000)
export type App = typeof app
On the client side:
// client.ts
import { edenTreaty } from '@elysiajs/eden'
import type { App } from './server'
const app = edenTreaty<App>('http://localhost:')
// Example of handling typed errors based on status code
const { data: nendoroid, error } = app.mirror.post({
id: 1895,
name: 'Skadi'
})