Project Structure¶
This chapter lays out the recommended directory structure for a Gaia-powered web application. Following a consistent layout makes the codebase navigable, keeps backend and frontend concerns separated, and ensures that new team members (or AI assistants) can orient themselves quickly.
High-Level Layout¶
The project is split into three top-level directories — backend/, frontend/, and sdk/ — plus shared configuration at the root.
my-gaia-app/
├── backend/
│ ├── main.py # FastAPI app factory + lifespan
│ ├── settings.py # Pydantic BaseSettings (reads .env)
│ ├── api/
│ │ ├── __init__.py
│ │ ├── routes.py # All FastAPI route handlers
│ │ └── dependencies.py # Dependency injection (session, client)
│ ├── clients/
│ │ ├── __init__.py
│ │ └── gaia_client.py # Thin wrapper or re-export of the SDK client
│ ├── models/
│ │ ├── __init__.py
│ │ ├── api_models.py # Request/response Pydantic models
│ │ └── domain_models.py # Internal domain types
│ ├── services/
│ │ ├── __init__.py
│ │ ├── auth_service.py # Session management logic
│ │ ├── query_service.py # Orchestration: ask, stream, refine
│ │ └── search_service.py # Exhaustive search logic
│ ├── storage/
│ │ ├── __init__.py
│ │ └── session_store.py # In-memory or SQLite session store
│ ├── utils/
│ │ ├── __init__.py
│ │ └── errors.py # Exception helpers, HTTP error mappers
│ ├── requirements.txt
│ └── .env.example
├── frontend/
│ ├── index.html
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── src/
│ ├── main.tsx # React entry point
│ ├── App.tsx # Root component + router
│ ├── api/
│ │ ├── client.ts # Base fetch wrapper with session header
│ │ └── gaia.ts # Typed Gaia API calls
│ ├── pages/
│ │ ├── LoginPage.tsx
│ │ ├── ChatPage.tsx
│ │ └── DatasetsPage.tsx
│ ├── components/
│ │ ├── ChatMessage.tsx
│ │ ├── ChatInput.tsx
│ │ ├── DatasetSelector.tsx
│ │ ├── DocumentViewer.tsx
│ │ └── LoginForm.tsx
│ ├── state/
│ │ ├── sessionStore.ts # Zustand store for session/auth
│ │ ├── chatStore.ts # Zustand store for messages
│ │ └── datasetStore.ts # Zustand store for datasets
│ ├── hooks/
│ │ ├── useAuth.ts
│ │ └── useStreaming.ts
│ └── styles/
│ └── globals.css
├── sdk/
│ └── python/
│ ├── gaia_sdk/ # The provided Python SDK
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── models.py
│ │ ├── exceptions.py
│ │ ├── streaming.py
│ │ └── auth.py
│ ├── setup.py
│ └── requirements.txt
├── .env # Root env (shared secrets)
├── .gitignore
├── docker-compose.yml
└── README.md
Backend Directory Breakdown¶
main.py¶
The FastAPI application factory. Creates the FastAPI instance, registers middleware (CORS), mounts routers, and defines the async lifespan context manager for startup/shutdown tasks.
settings.py¶
A single Pydantic BaseSettings class that reads all configuration from environment variables or a .env file. Every tunable — Gaia base URL, API key, CORS origins, timeouts — lives here.
api/routes.py¶
All HTTP route handlers. Each route is a thin function that validates input, calls into services/, and returns a Pydantic response model. Routes should contain minimal business logic.
api/dependencies.py¶
FastAPI dependency injection providers. These are functions that extract the session ID from request headers, look up the API key, and construct a GaiaClient — all available to routes via Depends().
clients/gaia_client.py¶
If you're using the provided SDK, this module simply re-exports GaiaClient from gaia_sdk. If you need customizations (extra headers, retry logic, caching), wrap the SDK client here.
models/¶
Pydantic models split into two files:
api_models.py— Shapes of your public API requests and responses.domain_models.py— Internal types (e.g.,Session,Principal) not exposed to callers.
services/¶
Business logic lives here. Each service module is a collection of pure-ish functions (or classes) that orchestrate calls to clients/ and storage/. Routes call services; services call clients.
storage/¶
Data persistence. For development, an in-memory dict is fine. For production, swap in SQLite, Redis, or a database. The session_store.py module manages session lifecycle (create, lookup, expire, delete).
utils/errors.py¶
Helper functions for mapping exceptions to HTTP responses. Centralizing error mapping keeps routes clean and ensures a consistent error envelope across all endpoints.
Frontend Directory Breakdown¶
api/client.ts¶
A thin fetch wrapper that attaches the X-Session-ID header to every request and provides typed get/post helper functions. All API communication flows through this module.
api/gaia.ts¶
Typed functions that call your backend API. Each function corresponds to a backend route — login(), ask(), listDatasets(), etc. — and returns typed responses.
pages/¶
Top-level route components. Each page corresponds to a URL path in react-router-dom. Pages compose components and call hooks; they should contain layout logic, not data-fetching details.
components/¶
Reusable UI components. These receive props and render UI. They should be as stateless as possible — state lives in Zustand stores and is accessed via hooks.
state/¶
Zustand stores. Each store manages a single domain:
sessionStore.ts— Session ID, login status, API key (transient).chatStore.ts— Messages, streaming state, conversation ID.datasetStore.ts— Available datasets, selected dataset(s).
hooks/¶
Custom React hooks that compose store access with side effects. For example, useAuth() wraps the session store with login/logout functions, and useStreaming() manages the SSE connection lifecycle.
styles/¶
Global CSS or Tailwind configuration. Component-specific styles can use CSS modules or Tailwind utility classes directly in JSX.
Initializing the Project from Scratch¶
Backend¶
mkdir -p my-gaia-app/backend
cd my-gaia-app/backend
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn httpx pydantic pydantic-settings python-dotenv
pip freeze > requirements.txt
mkdir -p api clients models services storage utils
touch api/__init__.py clients/__init__.py models/__init__.py \
services/__init__.py storage/__init__.py utils/__init__.py
Frontend¶
cd my-gaia-app
npm create vite@latest frontend -- --template react-ts
cd frontend
npm install zustand react-router-dom
npm install -D tailwindcss @tailwindcss/vite
mkdir -p src/api src/pages src/components src/state src/hooks src/styles
SDK¶
cd my-gaia-app
# Copy or symlink the SDK into your project
cp -r /path/to/build-with-gaia/sdk .
# Install as editable so imports resolve
pip install -e sdk/python/
Using the Provided SDK vs. Building Your Own Client¶
The gaia_sdk package shipped with this guide is the recommended way to interact with the Gaia API. It provides:
| Feature | SDK | DIY httpx Client |
|---|---|---|
| Typed request/response models | You build them | |
| Async context manager lifecycle | You build it | |
SSE streaming with ask_stream / ask_stream_iter | You parse SSE manually | |
| Typed exception hierarchy | You map status codes | |
| Auth header management | You build it |
When to wrap the SDK
If you need features the SDK doesn't provide — request retries, response caching, or custom middleware — create a thin wrapper in clients/gaia_client.py that delegates to the SDK. Don't fork the SDK itself.
Don't duplicate the client
Avoid having both a raw httpx client and the SDK client in the same project. Pick one and use it consistently. The SDK uses httpx under the hood, so you get the same HTTP behavior either way.
Next Steps¶
- Backend with FastAPI — Build the backend from
main.pyup. - The Gaia Client Library — Deep dive into the SDK.
- Frontend with React — Build the React frontend.