BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 12, 2026ReactTanStack Start

TanStack Start Streaming API Routes: Preserve the Response Body

Connect a BTST API handler to TanStack Start without buffering its response, losing authentication headers, or confusing the page and API mounts.

TanStack Start Streaming API Routes: Preserve the Response Body

A streaming endpoint can return HTTP 200 and still feel broken: the first text arrives only after generation finishes, the client cannot parse the response, or authenticated history calls lose their session. Start by checking how the framework route forwards the request and response.

In a BTST integration, the backend handler already owns the HTTP response. TanStack Start needs to route requests to that handler while preserving its body stream, status, and headers.

Mount the backend handler once#

For a backend configured at /api/data, the BTST 3.0.0 CLI template uses a TanStack catch-all server route:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
// src/routes/api/data/$.ts
import { createFileRoute } from "@tanstack/react-router";
import { toTanStackHandlers } from "@btst/stack/tanstack";
import { handler } from "@/lib/stack";

export const Route = createFileRoute("/api/data/$")({
  server: { handlers: toTanStackHandlers(handler) },
});

This assumes an existing TanStack Start project, its generated route types, the @/ alias, and a configured server stack exporting handler. Keep credentials and database setup in that server module. The snippet is the route integration, not a complete app.

The released BTST handler adapter forwards the original Request for GET, POST, PUT, PATCH, and DELETE. Each method returns the backend handler's result directly. It does not read the response into a string first.

TanStack's server-route documentation describes the server.handlers convention. Use a server route for this HTTP endpoint; the BTST browser client expects the plugin's HTTP contract.

Keep the API mount separate from the page mount#

The backend basePath and client api.basePath must identify the same API mount. Client site.basePath identifies the pages instead. For example:

ConfigurationExamplePurpose
Backend basePath/api/dataReceives plugin API requests
Client api.basePath/api/dataSends browser and loader API calls
Client site.basePath/pBuilds plugin page URLs
TanStack server route/api/data/$Forwards API requests to BTST

A chat page under /p does not imply that its stream should POST to the page URL. Inspect the actual request URL in the browser Network panel. If it returns your site's HTML shell, fix routing before changing the model configuration.

Preserve the stream and its protocol#

Do not add await response.text() or await response.json() around a streaming response just to log it. Those operations consume the body and wait for completion. Wrapping the consumed result in a new response can also discard status and protocol headers.

AI Chat in this release uses the AI SDK UI message stream. That carries structured chat events, not just arbitrary text chunks. Keep the plugin's matching client and server transport together; replacing one side with a plain-text reader changes the contract.

Middleware should also avoid consuming the incoming request body before BTST reads it. Forwarding the original request preserves the cookies and headers that the server identity resolver needs. Configure allowed origins and credentials deliberately for cross-origin deployments, and keep privileged headers out of browser-visible configuration.

Locate the delay before changing code#

Use this sequence on a development or staging conversation with non-sensitive input:

  1. Inspect the response status and content type. An HTML page, redirect, or authorization failure points to routing or access configuration.
  2. Observe when bytes arrive. Compare the direct application endpoint with the same endpoint through any production proxy. A buffering proxy and a slow model need different fixes.
  3. Check the browser's transport error. Bytes arriving does not prove that the client understands their event format.
  4. Complete the response and reload history. Streaming delivery and successful persistence are separate checks.
  5. Exercise cancellation and a failed model call so the interface does not leave a permanent loading state.

Avoid logging full request headers, prompts, or stream bodies during diagnosis. Status, elapsed time, a correlation ID, and a coarse failure category are usually enough to find which layer failed.

Use BTST installation for complete framework wiring and AI Chat documentation for model and persistence configuration. If history is the remaining problem, check private conversation ownership and transactions before changing the streaming adapter.

In This Post

Mount the backend handler onceKeep the API mount separate from the page mountPreserve the stream and its protocolLocate the delay before changing code