# How the Kaneo Web App Interacts with the Kaneo API: Architecture Deep Dive

> Explore the Kaneo web app's architecture. Learn how it interacts with the Kaneo API using a type-safe Hono client, OpenAPI, WebSocket, and TanStack Query for efficient data management and real-time updates.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: architecture
- Published: 2026-08-09

---

**The Kaneo web application communicates with its backend API through a type-safe Hono client that auto-generates TypeScript definitions from the OpenAPI schema, wrapped by specialized fetcher utilities and TanStack Query hooks for state management, with real-time updates delivered via WebSocket connections.**

The Kaneo project management platform implements a fully-typed communication layer between its React frontend and Hono-based backend to eliminate runtime contract mismatches. Understanding exactly how the kaneo web app interacts with the kaneo api reveals a sophisticated pattern of compile-time validation, centralized request configuration, and efficient data synchronization. This architecture lives in the `usekaneo/kaneo` monorepo, where shared TypeScript definitions ensure the frontend and backend remain in perfect alignment.

## The Type-Safe Hono Client

At the core of the communication layer sits the **Hono client**, generated from the shared OpenAPI contract (`AppType`). Located in [`packages/libs/src/hono.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/hono.ts), this client instance wraps the native `fetch` API and automatically injects required headers while handling cookie-based authentication.

```typescript
// packages/libs/src/hono.ts
export const client = hc<AppType>(apiUrl, {
  headers: {
    "Content-Type": "application/json",
    "X-Kaneo-Window-Id": windowId,
  },
  credentials: "include", // Enables cookie-based auth
});

```

The client configuration ensures every request includes the `Content-Type: application/json` and `X-Kaneo-Window-Id` headers, while `credentials: "include"` preserves authentication cookies across cross-origin requests. This singleton pattern guarantees consistent request formatting throughout the application.

## Dynamic API URL Construction

For endpoints not covered by the Hono client—such as WebSocket connections or custom routes—the frontend uses a dedicated helper function. The `getApiUrl` utility in [`apps/web/src/fetchers/get-api-url.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/get-api-url.ts) normalizes base URLs and constructs absolute paths.

```typescript
// apps/web/src/fetchers/get-api-url.ts
export function getApiUrl(path: string) {
  const baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
  return `${baseUrl}/${path.replace(/^\//, "")}`;
}

```

This utility is essential for environment-specific routing, allowing the same codebase to target development, staging, and production APIs without hardcoded URLs.

## Fetcher Functions and API Operations

Each API operation is encapsulated in a **thin async fetcher function** that either invokes the typed Hono client or uses native `fetch` with `getApiUrl`. These functions handle request serialization, response parsing, and error normalization.

```typescript
// apps/web/src/fetchers/project/create-project.ts
export async function createProject({ name, slug, workspaceId }: CreateProjectInput) {
  const response = await client.project.$post({
    json: { name, slug, workspaceId },
  });
  
  if (!response.ok) {
    throw new Error("Failed to create project");
  }
  
  return response.json();
}

```

Similar fetchers exist for tasks, workspaces, and user operations, creating a predictable interface where components never call the API directly. This abstraction layer simplifies testing and allows for centralized error handling and request logging.

## React Query Integration for State Management

The UI layer consumes these fetchers through **TanStack Query hooks** located under `apps/web/src/hooks/queries/`. These hooks provide caching, background refetching, and loading state management while maintaining type safety through the shared schema.

```typescript
// apps/web/src/hooks/queries/project/use-get-project.ts
import { useQuery } from "@tanstack/react-query";
import { getProject } from "@/fetchers/project/get-project";

export function useGetProject({ id, workspaceId }: GetProjectInput) {
  return useQuery({
    queryKey: ["projects", workspaceId, id],
    queryFn: () => getProject({ id, workspaceId }),
    enabled: Boolean(id && workspaceId),
  });
}

```

This pattern decouples data fetching from component logic, ensuring that multiple components referencing the same project share a single cached instance. The `queryKey` structure enables automatic cache invalidation when workspace or project identifiers change.

## Real-Time Updates via WebSocket

For live notifications and collaborative features, Kaneo establishes **WebSocket connections** alongside its REST API. The [`use-user-websocket.ts`](https://github.com/usekaneo/kaneo/blob/main/use-user-websocket.ts) and [`use-project-websocket.ts`](https://github.com/usekaneo/kaneo/blob/main/use-project-websocket.ts) hooks manage these persistent connections, using `getApiUrl("ws")` to resolve the WebSocket endpoint.

```typescript
// apps/web/src/hooks/use-user-websocket.ts
export function useUserWebsocket() {
  const [socket, setSocket] = useState<WebSocket | null>(null);
  
  useEffect(() => {
    const wsUrl = getApiUrl("ws").replace(/^http/, "ws");
    const ws = new WebSocket(wsUrl);
    
    ws.onopen = () => {
      console.log("WebSocket connected");
    };
    
    setSocket(ws);
    return () => ws.close();
  }, []);
  
  return socket;
}

```

On the backend, message broadcasting is handled by adapter classes in [`apps/api/src/ws/redis-broadcast-adapter.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/ws/redis-broadcast-adapter.ts) and [`apps/api/src/ws/in-memory-broadcast-adapter.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/ws/in-memory-broadcast-adapter.ts), enabling horizontal scaling through Redis or single-node deployments using memory-based pub/sub.

## Summary

The Kaneo web app interacts with its API through a layered architecture that prioritizes type safety and developer experience:

- **Hono client** ([`packages/libs/src/hono.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/hono.ts)) provides compile-time type checking via the `AppType` OpenAPI schema
- **Fetcher utilities** abstract HTTP requests and response handling for all CRUD operations
- **TanStack Query hooks** manage server state, caching, and synchronization across components
- **WebSocket connections** deliver real-time updates through the same URL resolution logic as REST endpoints
- **Cookie-based authentication** (`credentials: "include"`) maintains secure sessions without manual token management

This stack ensures that changes to the backend API surface immediately trigger TypeScript errors in the frontend, preventing deployment of mismatched contracts.

## Frequently Asked Questions

### How does Kaneo ensure type safety between the frontend and backend?

Kaneo generates a shared `AppType` TypeScript definition from the backend's OpenAPI specification. The Hono client in [`packages/libs/src/hono.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/hono.ts) uses this type to enforce correct request payloads and response shapes at compile time, eliminating the need for runtime validation libraries and preventing API contract drift.

### What authentication mechanism does the Kaneo web app use for API requests?

The application relies on **cookie-based session authentication** configured via `credentials: "include"` in the Hono client initialization. This approach stores HTTP-only session cookies that are automatically transmitted with each request, eliminating the need for manual JWT handling in the frontend code while protecting against XSS attacks.

### How does Kaneo handle real-time collaborative updates?

Real-time functionality is implemented through **WebSocket connections** established by hooks like [`use-user-websocket.ts`](https://github.com/usekaneo/kaneo/blob/main/use-user-websocket.ts) and [`use-project-websocket.ts`](https://github.com/usekaneo/kaneo/blob/main/use-project-websocket.ts). These connections target the `/ws` endpoint resolved through `getApiUrl`, while the backend uses broadcast adapters ([`apps/api/src/ws/redis-broadcast-adapter.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/ws/redis-broadcast-adapter.ts)) to distribute messages across all connected clients in a workspace.

### What is the purpose of the X-Kaneo-Window-Id header?

The `X-Kaneo-Window-Id` header identifies specific browser tab instances to the backend, enabling the API to track and manage WebSocket connections per client window rather than per user. This granularity allows the server to send targeted updates to specific tabs and manage connection lifecycle events accurately during multi-tab usage.