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 '@elysiajs/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 server and client.

  1. On server, Elysia is called directly without HTTP overhead.
  2. On 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.