Kaneo Development Best Practices: A Complete Guide to Contributing to the Self-Hosted Project Management Platform

The best practices for developing with Kaneo include maintaining the pnpm monorepo structure, using the controller pattern for API logic, validating inputs with Valibot, consuming data via TanStack Query fetchers on the frontend, and enforcing code quality through Biome linting and Conventional Commits.

Kaneo is a self-hosted project management platform built as a modern TypeScript monorepo. Whether you're extending the API, building new UI features, or deploying to production, following the established conventions in the usekaneo/kaneo repository ensures your contributions remain maintainable and consistent with the codebase. This guide covers the architectural patterns, tooling, and workflows that govern development across the API, web frontend, and shared packages.

Monorepo Structure and Package Management

Kaneo organizes code into a pnpm workspace orchestrated by TurboRepo. Understanding this layout is essential before making any changes.

Workspace Organization

The repository root contains three primary directories:

  • apps/ – Runnable services (API, web, documentation)
  • packages/ – Reusable libraries shared across apps
  • charts/ – Helm charts for Kubernetes deployment

This structure is documented in [CLAUDE.md](https://github.com/usekaneo/kaneo/blob/main/CLAUDE.md) and enforced by [pnpm-workspace.yaml](https://github.com/usekaneo/kaneo/blob/main/pnpm-workspace.yaml), which defines workspace boundaries.

Command Orchestration

Always run commands from the repository root so TurboRepo can parallelize builds and cache results:

pnpm dev          # Launches API and web in watch mode

pnpm lint         # Runs Biome linting with auto-fix

pnpm typecheck    # Type-checks every package

pnpm build        # Produces production bundles

API Development Patterns

The Kaneo API in apps/api follows a controller pattern that separates business logic from route handling, with strict input validation and automatic OpenAPI documentation.

Controller-Based Architecture

Place business logic in apps/api/src/<feature>/controllers/ and keep route files thin. For example, [apps/api/src/workspace/controllers/get-workspace-members.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/workspace/controllers/get-workspace-members.ts) demonstrates a clean database query pattern:

// Controllers contain pure business logic
const getWorkspaceMembers = async (workspaceId: string) => {
  // Database interaction via Drizzle ORM
  return db.query.workspaceMembers.findMany({
    where: eq(workspaceMembers.workspaceId, workspaceId),
  });
};

Route handlers apply validation, authentication, and OpenAPI metadata:

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

const example = new Hono<{ Variables: { userId: string } }>()
  .get(
    "/:id",
    describeRoute({
      operationId: "getExample",
      tags: ["Example"],
      description: "Fetch an example resource by ID",
    }),
    validator("param", v.object({ id: v.string() })),
    async (c) => {
      const { id } = c.req.valid("param");
      const result = await getExample(id);
      return c.json(result);
    },
  );

export default example;

Validation and Documentation

Authentication and Database

Access the current user in request handlers via c.get("user"), provided by Better Auth as configured in [apps/api/src/auth.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts).

Database models use Drizzle ORM in [apps/api/src/database/schema.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts), with conventions including:

  • CUID2 identifiers for all primary keys
  • createdAt and updatedAt timestamps on every table

Frontend Development with React and TanStack Query

The Kaneo web app (apps/web) follows predictable patterns for data fetching, state management, and component structure.

Data Fetching Architecture

Encapsulate API calls in fetchers under apps/web/src/fetchers/, then consume them through custom hooks in apps/web/src/hooks/queries/:

// apps/web/src/fetchers/example/get-example.ts
import { apiUrl } from "@/lib/api-url";

export async function getExample(id: string) {
  const res = await fetch(`${apiUrl}/example/${id}`, {
    credentials: "include",
  });
  if (!res.ok) throw new Error("Failed to fetch example");
  return res.json();
}
// apps/web/src/hooks/queries/example/useExample.ts
import { useQuery } from "@tanstack/react-query";
import { getExample } from "@/fetchers/example/get-example";

export function useExample(id: string) {
  return useQuery(["example", id], () => getExample(id));
}

See [apps/web/src/fetchers/workspace/get-workspaces.ts](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/workspace/get-workspaces.ts) for a production fetcher example.

State Management and UI

  • Zustand for global UI state, following patterns in apps/web/src/lib/
  • Radix UI primitives with Tailwind CSS v4 for accessible components
  • PascalCase naming convention; place components in apps/web/src/components/

Internationalization Requirements

All user-visible strings must use react-i18next. Add keys to [i18n/en-US.json](https://github.com/usekaneo/kaneo/blob/main/i18n/en-US.json) first, then reference via the t() function:

// apps/web/src/components/TaskGreeting.tsx
import { useTranslation } from "react-i18next";

export function TaskGreeting({ name }: { name: string }) {
  const { t } = useTranslation();
  return <p>{t("tasks:greeting", { name })}</p>;
}

The i18n workflow is detailed in [CONTRIBUTING.md](https://github.com/usekaneo/kaneo/blob/main/CONTRIBUTING.md).

Environment Configuration

Kaneo uses a single .env file at the repository root for both API and web services. Required variables include:

Variable Purpose
KANEO_CLIENT_URL Web app origin for redirects and CORS
KANEO_API_URL Public API endpoint
AUTH_SECRET Encryption key for Better Auth
PostgreSQL credentials Database connection

Complete documentation appears in [ENVIRONMENT_SETUP.md](https://github.com/usekaneo/kaneo/blob/main/ENVIRONMENT_SETUP.md).

CORS and Scaling

Code Quality and Testing Standards

Linting and Formatting with Biome

Biome is the sole tool for formatting and linting. Key rules enforced:

  • Double quotes everywhere
  • Semicolons required
  • Import ordering: external → internal (@/ alias) → relative

Run pnpm lint before committing; a pre-commit hook enforces this automatically.

Conventional Commits

Follow the Conventional Commits specification (feat:, fix:, docs:, etc.). The commit-msg hook in .husky/commit-msg validates format.

Testing Strategy

Test Type Location Command
Unit tests Alongside source (*.test.ts) pnpm test
Integration tests tests/api-integration/ pnpm test:integration

Each app maintains its own Vitest configuration: apps/api/vitest.config.ts and apps/web/vitest.config.ts. Integration tests use real PostgreSQL instances; see [tests/api-integration/project.test.ts](https://github.com/usekaneo/kaneo/blob/main/tests/api-integration/project.test.ts) for an example.

Key Files and References

Area File Purpose
Monorepo config [pnpm-workspace.yaml](https://github.com/usekaneo/kaneo/blob/main/pnpm-workspace.yaml) Workspace boundaries
API entry [apps/api/src/index.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts) Hono setup, middleware, routes
Controller pattern [apps/api/src/workspace/controllers/get-workspace-members.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/workspace/controllers/get-workspace-members.ts) DB query example
OpenAPI generator [apps/api/src/utils/openapi-spec.ts](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/openapi-spec.ts) Spec construction
Fetcher pattern [apps/web/src/fetchers/workspace/get-workspaces.ts](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/workspace/get-workspaces.ts) API client usage
Query hook [apps/web/src/hooks/queries/workspace/useWorkspaces.ts](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/hooks/queries/workspace/useWorkspaces.ts) TanStack Query integration
Shared utilities [packages/libs/src/api-url.ts](https://github.com/usekaneo/kaneo/blob/main/packages/libs/src/api-url.ts) Common API URL logic

Summary

  • Preserve monorepo structure: Use pnpm and TurboRepo commands from the repository root
  • Separate concerns: Controllers for business logic, thin Hono routes for validation and auth, fetchers with TanStack Query on the frontend
  • Validate rigorously: Valibot schemas on all inputs, OpenAPI documentation on all routes
  • Share code appropriately: Place reusable utilities in packages/libs
  • Configure environments correctly: Single .env file, proper CORS origins, optional Redis for scaling
  • Maintain quality standards: Biome formatting, Conventional Commits, comprehensive test coverage

Frequently Asked Questions

What package manager does Kaneo require?

Kaneo requires pnpm. The pnpm-workspace.yaml file defines workspace boundaries, and all development commands (pnpm dev, pnpm lint, pnpm build) must be run from the repository root so TurboRepo can orchestrate the monorepo correctly.

How should I structure a new API endpoint in Kaneo?

Create a controller file in apps/api/src/<feature>/controllers/ containing your database logic, then wire it into a thin Hono route in apps/api/src/<feature>/index.ts. Apply Valibot validation via the validator helper and document the route with describeRoute for automatic OpenAPI generation.

Encapsulate API calls in fetcher functions under apps/web/src/fetchers/, then consume them through custom hooks in apps/web/src/hooks/queries/ using TanStack Query. This separation keeps data fetching logic reusable and caching behavior explicit.

How do I add translations to the Kaneo interface?

Add new keys to [i18n/en-US.json](https://github.com/usekaneo/kaneo/blob/main/i18n/en-US.json) following the nested namespace structure, then reference them via t("namespace:key") from the useTranslation hook. All user-visible strings must go through react-i18next—hardcoded text is not permitted.

What testing is required before submitting a Kaneo contribution?

Run pnpm test for unit tests and pnpm test:integration for integration tests. Unit tests live alongside source files, while integration tests requiring PostgreSQL reside in tests/api-integration/. The pre-commit hook also ensures Biome linting passes and commit messages follow Conventional Commits.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →