End-to-end type safety
Imagine you have a toy train set.
Each piece of the train track has to fit perfectly with the next one, like puzzle pieces.
End-to-end type safety is like making sure all the pieces of the track match up correctly so the train doesn’t fall off or get stuck.
For a framework, having end-to-end type safety means that you can connect the client and server in a type-safe manner.
Elysia provides end-to-end type safety without code generation out of the box with an RPC-like connector, Eden.
Other frameworks that support e2e type safety:
- tRPC
- Remix
- SvelteKit
- Nuxt
- TS-Rest
Elysia allows you to change the type on the server, and it will be instantly reflected on the client, helping with auto-completion and type enforcement.
Eden is an RPC-like client to connect Elysia with end-to-end type safety using only TypeScript’s type inference instead of code generation.
It shares the server’s types with the client through type-only imports. No generated client needs to be kept in sync.
Eden consists of 2 modules:
- Eden Treaty (recommended): an improved RPC version of Eden Treaty 1 (edenTreaty)
- Eden Fetch: A fetch-like client with type safety
Below is an overview of each module.
Eden Treaty (recommended)
Section titled “Eden Treaty (recommended)”Eden Treaty is an object-like representation of an Elysia server providing end-to-end type safety and a significantly improved developer experience.
With Eden Treaty, we can interact with an Elysia server with full-type support and auto-completion, error handling with type narrowing, and create type-safe unit tests.
Example usage of Eden Treaty: server.ts
import { Elysia, t } from 'elysia'
const app = new Elysia() .get('/', 'hi') .get('/users', () => 'Skadi') .put('/nendoroid/:id', { body: t.Object({ name: t.String(), from: t.String() }) }, ({ body }) => body) .get('/nendoroid/:id/name', () => 'Skadi') .listen(3000)
export type App = typeof appindex.ts
import { treaty } from '@elysia/eden'import type { App } from './server'
const app = treaty<App>('localhost:3000')
// Call [GET] at '/'const { data } = await app.get()
// Call [PUT] at '/nendoroid/:id'const { data: nendoroid, error } = await app.nendoroid({ id: 1895 }).put({ name: 'Skadi', from: 'Arknights'})Eden Fetch
Section titled “Eden Fetch”A fetch-like alternative to Eden Treaty for developers who prefer fetch syntax.
For this example, export a server with the matching route:
import { Elysia, t } from 'elysia'
const app = new Elysia() .post('/name/:name', { body: t.Object({ branch: t.String(), type: t.String() }) }, ({ params, body }) => ({ name: params.name, ...body })) .listen(3000)
export type App = typeof appimport { edenFetch } from '@elysia/eden'import type { App } from './server'
const fetch = edenFetch<App>('http://localhost:3000')
const { data } = await fetch('/name/:name', { method: 'POST', params: { name: 'Saori' }, body: { branch: 'Arius', type: 'Striker' }})