# Kaneo Project Structure: A Complete Guide to the Monorepo Architecture

> Explore the Kaneo project structure, a pnpm monorepo with TurboRepo. Discover apps, packages, and Kubernetes charts for efficient development and deployment.

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

---

**Kaneo is organized as a pnpm monorepo built with TurboRepo, splitting into three main sections: runnable applications in `apps/`, shared libraries in `packages/`, and Kubernetes deployment charts in `charts/`.**

This article breaks down exactly how the [usekaneo/kaneo](https://github.com/usekaneo/kaneo) repository is structured. Whether you're contributing code, deploying to production, or exploring the codebase for patterns to borrow, understanding this layout is essential.

## Monorepo Layout Overview

Kaneo follows a standard **TurboRepo-managed pnpm monorepo** pattern with clear separation of concerns:

| Section | Path | Purpose |
|---------|------|---------|
| **Apps** | `apps/` | Runnable applications |
| **Packages** | `packages/` | Shared internal libraries |
| **Charts** | `charts/` | Helm charts for Kubernetes deployment |

This structure keeps dependencies isolated while enabling efficient code sharing and parallel builds.

## The Apps Directory: `apps/`

The `apps/` folder contains three distinct applications that form the complete Kaneo platform.

### API Backend (`apps/api/`)

The backend is a **Hono-based HTTP server** with PostgreSQL persistence.

Key architectural decisions:

- **Framework**: Hono for lightweight, Edge-compatible routing
- **ORM**: Drizzle ORM with schema defined in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts)
- **Authentication**: Better Auth for session management
- **Validation**: Valibot schemas enforced via `hono-openapi` decorators
- **Documentation**: Self-documenting OpenAPI specs via decorators

Feature routes follow a strict convention. Each domain lives in `apps/api/src/{feature}/` with this internal structure:

```

apps/api/src/label/
├── index.ts          # Hono router with route definitions

└── controllers/
    ├── get-label.ts  # Business logic implementation

    └── ...

```

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

### Web Frontend (`apps/web/`)

The frontend is a **React 19+ SPA** with modern data-fetching patterns.

Technology stack:

- **Build tool**: Vite for fast HMR and optimized builds
- **Routing**: TanStack Router with file-based routing in `apps/web/src/routes/`
- **Data fetching**: TanStack Query with fetchers in `apps/web/src/fetchers/{feature}/`
- **State management**: Zustand for client-side state
- **UI components**: Radix UI primitives styled with Tailwind CSS v4

Route components import from fetchers, following this pattern:

```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();
}

```

The corresponding hook wraps this fetcher with caching:

```typescript
// 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));
}

```

### Documentation Site (`apps/docs/`)

Powered by Next.js, this hosts the public documentation and guides.

## The Packages Directory: `packages/`

Shared code lives in `packages/` and is consumed by multiple apps via pnpm workspaces.

| Package | Path | Contents |
|---------|------|----------|
| `email` | `packages/email/` | Email-sending utilities and templates |
| `libs` | `packages/libs/` | Reusable code including shared TypeScript types and utility functions; exposed via [`packages/libs/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/index.ts) |
| `typescript-config` | `packages/typescript-config/` | Centralized [`tsconfig.json`](https://github.com/usekaneo/kaneo/blob/main/tsconfig.json) presets for consistent compiler settings across the monorepo |

This approach eliminates duplication while maintaining strict versioning boundaries.

## The Charts Directory: `charts/`

Production deployment assets include **Helm charts** for Kubernetes. The [`charts/kaneo/Chart.yaml`](https://github.com/usekaneo/kaneo/blob/main/charts/kaneo/Chart.yaml) file defines chart metadata for deploying the full Kaneo stack.

## Database Schema Architecture

The Drizzle ORM schema in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts) enforces consistent patterns:

```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)]);

```

Key conventions:

- **Primary keys**: CUID2 via `createId()` for collision-resistant identifiers
- **Timestamps**: Every table includes `createdAt` and `updatedAt`
- **Foreign keys**: Explicit `onDelete` and `onUpdate` cascade rules
- **Indexing**: Named indexes for query performance
- **Relations**: Declared separately in [`apps/api/src/database/relations.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/relations.ts)

## Running the Kaneo Monorepo

Local development uses standard pnpm commands:

```bash

# Install all dependencies

pnpm install

# Start both API and Web in dev mode (via TurboRepo)

pnpm dev

```

TurboRepo orchestrates the dev servers, rebuilding only what changed.

## Critical Files to Know

| File | Significance |
|------|--------------|
| [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts) | Source of truth for all database tables |
| [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts) | Backend entry point—routers, middleware, and CORS configuration |
| [`apps/web/src/App.tsx`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/App.tsx) | Frontend root component with provider setup |
| [`packages/libs/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/index.ts) | Shared utilities exported for cross-app use |
| [`charts/kaneo/Chart.yaml`](https://github.com/usekaneo/kaneo/blob/main/charts/kaneo/Chart.yaml) | Deployment metadata for Kubernetes |

## Summary

- Kaneo uses a **pnpm monorepo with TurboRepo** for build orchestration
- Three apps in `apps/`: **API** (Hono/PostgreSQL), **Web** (React/Vite), and **Docs** (Next.js)
- Shared utilities in `packages/` including email, common libraries, and TypeScript configs
- **Kubernetes deployment** via Helm charts in `charts/`
- Consistent patterns: feature-based folders, Valibot validation, file-based routing, and CUID2 primary keys

## Frequently Asked Questions

### What build system does Kaneo use?

Kaneo uses **pnpm workspaces** combined with **TurboRepo**. This enables efficient task running across the monorepo with intelligent caching and parallel execution. The `pnpm dev` command starts both API and web servers simultaneously.

### Why does Kaneo use Hono instead of Express?

According to the Kaneo source code, **Hono** provides Edge-compatible routing with TypeScript-first design and minimal overhead. It integrates cleanly with `hono-openapi` for automatic documentation and runs efficiently in serverless environments.

### How are database changes managed in Kaneo?

Database schema changes are handled through **Drizzle ORM** with migrations generated from [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts). The schema uses CUID2 for IDs, enforces cascade rules on relations, and includes performance indexes. Migration commands would run via Drizzle Kit (standard for Drizzle projects).

### Where should new API endpoints be added?

New endpoints belong in `apps/api/src/{feature}/` following the existing pattern: create a [`index.ts`](https://github.com/usekaneo/kaneo/blob/main/index.ts) router with `hono-openapi` decorators, place business logic in `controllers/`, and validate inputs with **Valibot** schemas. Register the router in [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts).