Atlas

TanStack Start

Integrate Atlas into a TanStack Start app using plain fetch or TanStack Query.

Integrate Atlas into a TanStack Start app using plain fetch or TanStack Query.

TanStack Start is a full-stack React framework. Unlike the other BYOF guides, this one does not use @ai-sdk/react -- it shows how to consume the Atlas API directly, which works with any HTTP client.

Prerequisites: A running Atlas API server. See Bring Your Own Frontend for architecture and common setup.


1. Install dependencies

For the synchronous approach:

bun add @tanstack/react-query

No AI SDK packages are required. If you prefer the useChat hook, you can install @ai-sdk/react and follow the React Vite guide -- it works the same in TanStack Start.

2. Configure the API URL

TanStack Start uses Vinxi (Vite-based). Configure the proxy in app.config.ts:

// app.config.ts
import { defineConfig } from "@tanstack/react-start/config";

export default defineConfig({
  vite: {
    server: {
      proxy: {
        "/api": {
          target: "http://localhost:3001",
          changeOrigin: true,
        },
      },
    },
  },
});

Option B: Cross-origin

# .env
VITE_ATLAS_API_URL=http://localhost:3001
# Atlas API .env
ATLAS_CORS_ORIGIN=http://localhost:3000

3. Synchronous queries with TanStack Query

The simplest integration uses the JSON query endpoint (POST /api/v1/query). No streaming protocol to parse.

// src/lib/atlas.ts
const API_URL = import.meta.env.VITE_ATLAS_API_URL ?? "";

export interface AtlasQueryResult {
  answer: string;
  sql: string[];
  data: Array<{ columns: string[]; rows: Record<string, unknown>[] }>;
  steps: number;
  usage: { totalTokens: number };
  conversationId?: string;
  pendingActions?: Array<{
    id: string;
    type: string;
    target: string;
    summary: string;
    approveUrl: string;
    denyUrl: string;
  }>;
}

export async function queryAtlas(
  question: string,
  opts?: { apiKey?: string; signal?: AbortSignal; conversationId?: string }
): Promise<AtlasQueryResult> {
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
  };
  if (opts?.apiKey) headers["Authorization"] = `Bearer ${opts.apiKey}`;

  const res = await fetch(`${API_URL}/api/v1/query`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      question,
      ...(opts?.conversationId && { conversationId: opts.conversationId }),
    }),
    signal: opts?.signal,
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw new Error(body.message ?? `Atlas query failed: ${res.status}`);
  }

  return res.json();
}

Tip: For a fully typed client with error handling, conversation management, and more, use the @useatlas/sdk package instead of hand-rolling fetch calls:

import { createAtlasClient } from "@useatlas/sdk";

const atlas = createAtlasClient({
  baseUrl: API_URL || window.location.origin,
  apiKey: "your-api-key",
});

const result = await atlas.query("How many users signed up last week?");
console.log(result.answer);       // narrative answer
console.log(result.sql);          // SQL queries executed
console.log(result.data);         // { columns, rows }[]
console.log(result.conversationId); // for follow-up queries

Query hook

// src/hooks/use-atlas-query.ts
import { useMutation } from "@tanstack/react-query";
import { queryAtlas, type AtlasQueryResult } from "../lib/atlas";

export function useAtlasQuery(apiKey?: string) {
  return useMutation<AtlasQueryResult, Error, string>({
    mutationFn: (question) => queryAtlas(question, { apiKey }),
  });
}

4. Rendering tool calls

If you use the @ai-sdk/react useChat approach, see the React/Vite guide for a complete ToolPart implementation -- it works identically in TanStack Start. The data shapes are the same across all frameworks:

  • executeSQL output -- { success, columns, rows, truncated } on success, or { success: false, error } on failure. Use rows.length to get the row count.
  • explore output -- a plain string (stdout of the command).
  • state -- check for "output-available" to know when the result is ready.

For the TanStack Query approach, the sync endpoint returns data in the data array directly -- no tool part parsing needed. See the Data Stream Protocol section for details on both response formats.

5. Which approach to choose

ApproachStreamingTool cardsComplexity
TanStack Query + /api/v1/queryNoTable from data fieldSimplest
Plain fetch streamText onlyManual parsingMedium
@ai-sdk/react useChatFullBuilt-in message partsFull featured

For most TanStack Start apps, start with the TanStack Query approach. Add @ai-sdk/react later if you need real-time streaming with tool call visualization.

On this page