# Kaneo Project Structure Explained: A Deep Dive into the Monorepo Architecture

> Explore the Kaneo project structure, a pnpm monorepo with TurboRepo. Learn how apps, packages, and charts organize the usekaneo/kaneo repository for efficient development.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: deep-dive
- Published: 2026-08-10

---

**Kaneo is organized as a pnpm monorepo managed by TurboRepo, dividing code into three main directories—`apps/` for runnable applications, `packages/` for shared libraries, and `charts/` for Kubernetes deployment assets.**

The Kaneo repository follows a clean architectural separation between backend services and frontend interfaces while maintaining strict type safety across the entire stack. This open-source project demonstrates modern TypeScript monorepo patterns by isolating concerns into distinct workspaces that share common tooling and configuration.

## Monorepo Layout and Directory Structure

Kaneo adopts a standard Turborepo-style organization where functionality is split between applications, shared packages, and infrastructure definitions.

### Apps Directory (`apps/`)

The `apps/` folder contains three runnable applications that form the complete system:

- **`apps/api/`** – Backend service built with Hono, PostgreSQL (via Drizzle ORM), and Better Auth. Feature routes live under `apps/api/src/{feature}/`, with each module exposing thin Hono handlers that delegate to controllers.
- **`apps/web/`** – Front-end Single Page Application using React 19+, Vite, TanStack Router, and TanStack Query. UI components reside in `apps/web/src/components/` and follow a file-based routing convention under `apps/web/src/routes/`.
- **`apps/docs/`** – Documentation site powered by Next.js.

### Packages Directory (`packages/`)

Shared libraries used across applications live here:

- **`packages/email/`** – Helper functions for sending emails.
- **`packages/libs/`** – Reusable code including type definitions and utility functions exported from [`packages/libs/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/index.ts).
- **`packages/typescript-config/`** – Centralized `tsconfig` files ensuring consistent TypeScript settings across the monorepo.

### Charts Directory (`charts/`)

The `charts/kaneo/` path contains Helm charts for deploying Kaneo on Kubernetes, with [`Chart.yaml`](https://github.com/usekaneo/kaneo/blob/main/Chart.yaml) defining the deployment metadata.

## Backend Architecture and API Structure

The Kaneo API follows a strict feature-based organization pattern that promotes modularity and clear separation of concerns.

### Feature-Based Route Organization

Routes are grouped by domain (e.g., `apps/api/src/label/`), each containing a Hono router that validates inputs using **Valibot** schemas and documents endpoints via **hono-openapi** decorators. Controllers handle business logic in separate `controllers/` subdirectories.

Here is the skeleton for a feature module:

```typescript
// apps/api/src/label/index.ts
import { Hono } from "hono";
import { describeRoute, validator } from "hono-openapi";
import * as v from "valibot";
import getLabel from "./controllers/get-label";

const label = new Hono<{ Variables: { userId: string } }>()
  .get("/:id",
    describeRoute({
      operationId: "getLabel",
      tags: ["Label"],
      description: "Fetch a label by its ID",
    }),
    validator("param", v.object({ id: v.string() })),
    async (c) => {
      const { id } = c.req.valid("param");
      const label = await getLabel(id);
      return c.json(label);
    }
  );
export default label;

```

The main entry point at [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts) wires together Hono, CORS, authentication, and registers all feature routers.

### Database Schema with Drizzle ORM

Database definitions are centralized in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts) using Drizzle ORM. Primary keys use CUID2 for collision-resistant identifiers, and every table includes `createdAt` and `updatedAt` timestamps. Relations are declared separately in [`apps/api/src/database/relations.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/relations.ts).

Example table definition:

```typescript
// apps/api/src/database/schema.ts
export const exampleTable = pgTable("example", {
  id: text("id").$defaultFn(() => createId()).primaryKey(),
  projectId: text("project_id")
    .notNull()
    .references(() => projectTable.id, { onDelete: "cascade", onUpdate: "cascade" }),
  title: text("title").notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" })
    .defaultNow()
    .$onUpdate(() => new Date())
    .notNull(),
}, (t) => [index("example_projectId_idx").on(t.projectId)]);

```

## Frontend Architecture and React Patterns

The web application uses modern React patterns with strict TypeScript integration across the monorepo boundaries.

### File-Based Routing and Data Fetching

Routing lives under `apps/web/src/routes/` and is handled by TanStack Router. Data fetching is abstracted into feature-specific fetchers located in `apps/web/src/fetchers/{feature}/`, while caching and server-state management use TanStack Query.

Example fetcher and corresponding hook:

```typescript
// apps/web/src/fetchers/label/get-label.ts
import { apiClient } from "@/lib/api-client";

export async function getLabel(labelId: string) {
  const resp = await apiClient.get(`/label/${labelId}`);
  return resp.json();
}

// apps/web/src/hooks/queries/label/use-label.ts
import { useQuery } from "@tanstack/react-query";
import { getLabel } from "@/fetchers/label/get-label";

export function useLabel(labelId: string) {
  return useQuery(["label", labelId], () => getLabel(labelId));
}

```

### State Management and UI Components

Client-side state management uses Zustand, while UI components are built on **Radix UI** primitives and styled with **Tailwind CSS v4**. This combination provides accessible, unstyled components with flexible styling capabilities.

## Shared Packages and Configuration

The `packages/libs/` workspace exports shared utilities and TypeScript types used by both `apps/api` and `apps/web`, ensuring type safety across the API boundary. The `packages/typescript-config/` workspace provides standardized [`tsconfig.json`](https://github.com/usekaneo/kaneo/blob/main/tsconfig.json) files that enforce strict compiler settings across all applications.

## Summary

- Kaneo uses a **pnpm monorepo** managed by TurboRepo with clear separation between `apps/`, `packages/`, and `charts/`.
- The **backend** (`apps/api/`) organizes code by feature, using Hono for HTTP handling, Valibot for validation, and Drizzle ORM for database operations with CUID2 primary keys.
- The **frontend** (`apps/web/`) uses React 19 with TanStack Router for file-based routing, TanStack Query for server state, and Zustand for client state.
- **Shared packages** provide centralized TypeScript configurations and reusable utilities accessible across all applications.
- **Helm charts** in `charts/kaneo/` provide Kubernetes deployment specifications.

## Frequently Asked Questions

### What build tool manages the Kaneo monorepo?

Kaneo uses **Turborepo** to orchestrate builds, tests, and development tasks across the pnpm workspace. This enables efficient caching and parallel execution of tasks defined in the [`turbo.json`](https://github.com/usekaneo/kaneo/blob/main/turbo.json) configuration, ensuring that changes in shared packages trigger rebuilds in dependent applications.

### How does Kaneo handle database migrations and schema changes?

Schema definitions live in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts) using Drizzle ORM. The project uses Drizzle Kit to generate and apply migrations based on changes to the TypeScript schema definitions. Relations are maintained separately in [`apps/api/src/database/relations.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/relations.ts) to keep the schema file focused on table structures.

### Where are authentication and authorization implemented in the Kaneo project structure?

Authentication logic resides in [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts), where Better Auth is integrated with the Hono application. Middleware applied at the router level injects `userId` into the context variables (as seen in `Hono<{ Variables: { userId: string } }>`), making user identification available to all downstream feature controllers without repeating auth logic in each route file.

### How do I run the complete Kaneo stack locally?

Use pnpm to install dependencies and start both applications simultaneously:

```bash

# Install all dependencies across the monorepo

pnpm install

# Start API and Web in development mode

pnpm dev

```

This command leverages Turborepo's pipeline to run the API server and Vite dev server in parallel, with hot reloading enabled for both backend and frontend code.