Kaneo Project Structure: A Complete Guide to the Monorepo Architecture
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 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 - Authentication: Better Auth for session management
- Validation: Valibot schemas enforced via
hono-openapidecorators - 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 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:
// 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:
// 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 |
typescript-config |
packages/typescript-config/ |
Centralized 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 file defines chart metadata for deploying the full Kaneo stack.
Database Schema Architecture
The Drizzle ORM schema in apps/api/src/database/schema.ts enforces consistent patterns:
// 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
createdAtandupdatedAt - Foreign keys: Explicit
onDeleteandonUpdatecascade rules - Indexing: Named indexes for query performance
- Relations: Declared separately in
apps/api/src/database/relations.ts
Running the Kaneo Monorepo
Local development uses standard pnpm commands:
# 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 |
Source of truth for all database tables |
apps/api/src/index.ts |
Backend entry point—routers, middleware, and CORS configuration |
apps/web/src/App.tsx |
Frontend root component with provider setup |
packages/libs/src/index.ts |
Shared utilities exported for cross-app use |
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. 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →