Skip to content
Our Sponsors
Open in Anthropic

Integration with TanStack Start ​

Elysia can run inside TanStack Start server routes.

  1. Create src/routes/api.$.ts
  2. Define an Elysia server
  3. Export Elysia handler in server.handlers
typescript
import { Elysia } from 'elysia'

import { createFileRoute } from '@tanstack/react-router'
import { createIsomorphicFn } from '@tanstack/react-start'

const app = new Elysia({
	prefix: '/api'
}).get('/', 'Hello Elysia!')

const handle = ({ request }: { request: Request }) => app.fetch(request) 

export const Route = createFileRoute('/api/$')({
	server: {
		handlers: {
			GET: handle, 
			POST: handle 
		}
	}
})

Elysia should now be running on /api.

We may add additional methods to server.handlers to support other HTTP methods as needed.

pnpm ​

If you use pnpm, pnpm doesn't auto install peer dependencies by default forcing you to install additional dependencies manually.

bash
pnpm add @sinclair/typebox openapi-types

Eden ​

We can add Eden for end-to-end type safety similar to tRPC.

typescript
import { Elysia } from 'elysia'
import { treaty } from '@elysia/eden'

import { createFileRoute } from '@tanstack/react-router'
import { createIsomorphicFn } from '@tanstack/react-start'

const app = new Elysia({
	prefix: '/api'
}).get('/', 'Hello Elysia!')

const handle = ({ request }: { request: Request }) => app.fetch(request)

export const Route = createFileRoute('/api/$')({
	server: {
		handlers: {
			GET: handle,
			POST: handle
		}
	}
})

export const getTreaty = createIsomorphicFn() 
	.server(() => treaty(app).api) 
	.client(() => treaty<typeof app>('localhost:3000').api) 

Notice that we use createIsomorphicFn to create a separate Eden Treaty instance for both the server and client:

  1. On the server, Elysia is called directly without HTTP overhead.
  2. On the client, we call the Elysia server through HTTP.

In a React component, we can use getTreaty to call the Elysia server with type safety.

Loader Data ​

Tanstack Start supports Loader to fetch data before rendering the component.

tsx
import { createFileRoute } from '@tanstack/react-router'

import { getTreaty } from './api.$'

export const Route = createFileRoute('/a')({
	component: App,
	loader: () => getTreaty().get().then((res) => res.data) 
})

function App() {
	const data = Route.useLoaderData() 

	return data
}

Calling Elysia in a loader executes it on the server during SSR and doesn’t incur HTTP overhead. When navigating from one page to another, the loader will run on the client-side, making an HTTP request to the endpoint.

Eden Treaty will ensure type safety on both server and client.

React Query ​

We can also use React Query to interact with Elysia server on client.

tsx
import { createFileRoute } from '@tanstack/react-router'
import { useQuery } from '@tanstack/react-query'

import { getTreaty } from './api.$'

export const Route = createFileRoute('/a')({
	component: App
})

function App() {
	const { data: response } = useQuery({ 
		queryKey: ['get'], 
		queryFn: () => getTreaty().get() 
	}) 

	return response?.data
}

This can work with any React Query features like caching, pagination, infinite queries, etc.


Please visit TanStack Start Documentation for more information about TanStack Start.