claude-start-cf

Server Routes (API Endpoints)

Server routes are HTTP endpoints defined in your route files using the server property. They live alongside your page routes and follow the same file-based conventions.

Routes in this demo

File path → endpoint URL

routes/api/hello.ts
GET POST/api/hello
routes/api/echo.ts
POST/api/echo
routes/api/users/$userId.ts
GET/api/users/:userId
routes/server-routes.tsx
POST + component/server-routes

GET /api/hello

routes/api/hello.ts → GET handler

A basic GET endpoint. Reads a query parameter and returns plain text. The simplest server route possible.

GET: async ({ request }) => {
  const url = new URL(request.url)
  const name = url.searchParams.get('name') || 'World'
  return new Response(`Hello, ${name}!`)
}

POST /api/hello

routes/api/hello.ts → POST handler

Same file, different HTTP method. Parses JSON body with request.json() and returns a JSON response using Response.json().

POST: async ({ request }) => {
  const body = await request.json()
  return Response.json({
    message: `Hello, ${body.name}!`,
    receivedAt: new Date().toISOString(),
  })
}

POST /api/echo

routes/api/echo.ts → POST handler

Echoes back the request body, content type, method, and URL. Demonstrates reading request headers and setting custom response headers.

GET /api/users/:userId

routes/api/users/$userId.ts → GET handler

Dynamic path params work the same as page routes. The $userId segment is captured and available via params.userId. Try IDs 1-3 for valid users, anything else for a 404.

GET: async ({ params }) => {
  const user = mockUsers[params.userId]
  if (!user) {
    return Response.json(
      { error: 'User not found', userId: params.userId },
      { status: 404 },
    )
  }
  return Response.json(user)
}

Combined: server handler + component

routes/server-routes.tsx → POST handler + component

This page itself has a POST server handler. The same file defines both the UI component you're reading and an API endpoint. Browser navigation renders the component; a POST request hits the handler.

export const Route = createFileRoute('/server-routes')({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const body = await request.json()
        return Response.json({ message: '...', received: body })
      },
    },
  },
  component: ServerRoutesPage, // Same file!
})

File conventions

Same rules as page routes

Server routes follow the same file-based naming as TanStack Router page routes.

FileEndpointType
api/hello.ts/api/helloStatic
api/users/$id.ts/api/users/:idDynamic param
api/file/$.ts/api/file/*Wildcard (splat)
feed[.]json.ts/feed.jsonEscaped dot
page.tsx/pageCombined (handler + component)

Server routes vs server functions: Use server routes when you need raw HTTP endpoints (webhooks, APIs, file downloads). Use createServerFn when you need typed RPC calls from your components and loaders.