Eden Treaty
Eden Treaty is an object representation used to interact with a server and features type safety, auto-completion, and error handling.
Read a route
Section titled “Read a route”A path becomes properties on the client, followed by its HTTP method. api.hi.get() calls GET /hi and returns Hi Elysia.
Here, passing the instance directly keeps the call in the same process. No listening server is needed.
import { Elysia } from 'elysia'import { treaty } from '@elysia/eden'
const app = new Elysia().get('/hi', 'Hi Elysia')const api = treaty(app)
const { data, error } = await api.hi.get()if (error) throw errorconsole.log(data)Pass a path parameter
Section titled “Pass a path parameter”Call a dynamic segment with an object containing its parameter name. api.id({ id: 1895 }).get() calls GET /id/1895.
TypeScript knows which parameter the route accepts.
import { Elysia } from 'elysia'import { treaty } from '@elysia/eden'
const app = new Elysia() .get('/hi', 'Hi Elysia') .get('/id/:id', ({ params: { id } }) => id)const api = treaty(app)
const { data, error } = await api.id({ id: 1895 }).get()if (error) throw errorconsole.log(data)Send a body
Section titled “Send a body”Pass a body to a method such as post. The route’s schema guides the client: this request needs a numeric id and a string name.
The server returns the same object. Check error before using data.
import { Elysia, t } from 'elysia'import { treaty } from '@elysia/eden'
const app = new Elysia() .get('/hi', 'Hi Elysia') .get('/id/:id', ({ params: { id } }) => id) .post('/mirror', { body: t.Object({ id: t.Number(), name: t.String() }) }, ({ body }) => body)const api = treaty(app)
const { data, error } = await api.mirror.post({ id: 1895, name: 'Skadi' })if (error) throw errorconsole.log(data)To call a separately running server, first export your existing Elysia server’s type:
import { Elysia, t } from 'elysia'
const app = new Elysia() .get('/hi', () => 'Hi Elysia') .get('/id/:id', ({ params: { id } }) => id) .post('/mirror', { body: t.Object({ id: t.Number(), name: t.String() }) }, ({ body }) => body) .listen(3000)
export type App = typeof appThen import the server type and consume the Elysia API on the client:
client.ts
import { treaty } from '@elysia/eden'import type { App } from './server'
const app = treaty<App>('localhost:3000')
// response type: 'Hi Elysia'const { data, error } = await app.hi.get()Tree-like syntax
Section titled “Tree-like syntax”An HTTP path is a resource indicator for a file system tree.
A file system consists of multiple levels of folders, for example:
- /documents/elysia
- /documents/kalpas
- /documents/kelvin
Each level is separated by / (slash) and a name.
However, in JavaScript, instead of using ”/” (slash), we use ”.” (dot) to access deeper resources.
Eden Treaty turns an Elysia server into a tree-like file system that can be accessed from the JavaScript frontend instead.
| Path | Treaty |
|---|---|
| / | |
| /hi | .hi |
| /deep/nested | .deep.nested |
Combined with the HTTP method, we can interact with the Elysia server.
| Path | Method | Treaty |
|---|---|---|
| / | GET | .get() |
| /hi | GET | .hi.get() |
| /deep/nested | GET | .deep.nested.get() |
| /deep/nested | POST | .deep.nested.post() |
Dynamic path
Section titled “Dynamic path”Dynamic path parameters need a name as well as a value. For an api client whose server registers /item/:name, use a function call to identify the parameter.
// Unclear what the value is supposed to represent?api.item['skadi'].get()This property-access form belongs to the legacy Eden Treaty 1 client; in Eden Treaty 2 it is a type error.
To handle this, we can specify a dynamic path using a function to provide a key value instead.
// Clear that the value is dynamic and the path is 'name'api.item({ name: 'Skadi' }).get()| Path | Treaty |
|---|---|
| /item | .item |
| /item/:name | .item({ name: 'Skadi' }) |
| /item/:name/id | .item({ name: 'Skadi' }).id |