How the Kaneo API Is Structured Within the Monorepo: A Layered Hono Architecture
The Kaneo API is organized as a modular Hono application under apps/api/src, using a hierarchical router pattern that separates global middleware, domain-specific feature routers, and pure-function controllers interacting with Drizzle ORM.
The Kaneo project (usekaneo/kaneo) implements a clean, scalable backend architecture within its Turborepo-based monorepo. The API layer follows a strict separation of concerns: a thin root application handles cross-cutting concerns like CORS and authentication, while feature-specific modules own their own routing and business logic. This design enables independent development of domains such as tasks, comments, and workspaces without coupling to the core application bootstrap.
Root Application and Global Middleware
The entry point for the API is apps/api/src/index.ts, which exports a createApp() function. This factory instantiates a top-level Hono<AppVariables>() instance and installs global middleware before any routes are mounted.
According to the source code, the root app configures:
- CORS handling for cross-origin requests
- Error handling wrappers for consistent JSON error responses
- WebSocket adapter setup for upgrade handling
- Authentication middleware (
authenticateApiRequest) applied globally
The root app then mounts the API sub-router using app.route("/api", api), creating the /api path prefix that all public endpoints share.
The API Sub-Router Pattern
Within apps/api/src/index.ts, a dedicated api router is instantiated as new Hono<ApiVariables>(). This sub-router acts as a collector for all domain-specific routes and exposes several meta-endpoints:
- Health check endpoints for monitoring
- Asset download routes for file serving
- The OpenAPI specification at
/api/openapi
The API router applies authentication middleware to all its routes via api.use("*", async (c, next) => { await authenticateApiRequest(c); ... }), ensuring every request is validated before reaching feature handlers.
Feature-Based Router Architecture
Each business domain (comment, task, project, workspace) lives in its own directory under apps/api/src/, following a consistent pattern. For example, the comment module is defined in apps/api/src/comment/index.ts, which exports a Hono router instance configured with CRUD operations.
Feature routers are mounted onto the API router in the main index file:
// apps/api/src/index.ts (excerpt)
import comment from "./comment";
import task from "./task";
import project from "./project";
api.route("/comment", comment);
api.route("/task", task);
api.route("/project", project);
Each feature router uses hono-openapi decorators for automatic documentation generation. The router in apps/api/src/comment/index.ts demonstrates this pattern by combining route definitions with describeRoute(), validator() (using Valibot schemas), and resolver utilities to produce type-safe, documented endpoints.
Controller Layer and Database Access
Controllers are pure functions stored in controllers/ subdirectories within each feature folder. They encapsulate all database interactions using Drizzle ORM and return plain data objects.
For instance, apps/api/src/comment/controllers/create-comment.ts handles the insertion logic, importing schema definitions from apps/api/src/database/schema.ts. This separation ensures that routers remain thin—responsible only for HTTP concerns like validation and serialization—while controllers manage business logic and data persistence.
// apps/api/src/comment/index.ts (pattern example)
import { Hono } from "hono";
import { describeRoute, validator } from "hono-openapi";
import * as v from "valibot";
import createComment from "./controllers/create-comment";
const comment = new Hono()
.post(
"/",
describeRoute({ operationId: "createComment", tags: ["Comment"] }),
validator("json", v.object({ content: v.string(), taskId: v.string() })),
async (c) => {
const body = c.req.valid("json");
const result = await createComment(body);
return c.json(result, 201);
}
);
export default comment;
Authentication and Shared Utilities
The apps/api/src/utils/authenticate-api-request.ts file exports the central authentication middleware used across the API. This utility validates session tokens or API keys extracted from the Authorization header and populates the Hono context with user and workspace information.
Additional utilities handle workspace validation, asset streaming, and OpenAPI normalization. These helpers are imported by feature routers as needed, maintaining DRY principles while keeping the root application configuration uncluttered.
WebSocket Support
Real-time functionality is handled by a separate sub-router defined in apps/api/src/ws/index.ts. This router manages HTTP upgrade requests to WebSocket connections under the /ws/* path.
The WebSocket layer reuses the same authenticateApiRequest middleware to validate connections before upgrading, ensuring that real-time channels respect the same security boundaries as the REST API.
OpenAPI Integration
The API automatically generates an OpenAPI 3.0 specification by traversing the decorated routes. The openAPIRouteHandler (referenced in the main index) merges route metadata from all feature routers and serves the combined spec at /api/openapi.
This integration means that adding describeRoute() metadata to a new endpoint immediately updates the public API documentation without manual YAML maintenance.
Summary
- The root app in
apps/api/src/index.tsbootstraps the Hono instance and configures global middleware including CORS and authentication. - An API sub-router mounted at
/apicollects all domain routes and provides health checks and OpenAPI serving. - Feature routers (e.g.,
comment,task,project) live in dedicated directories and define their own CRUD endpoints usinghono-openapifor documentation. - Controllers are pure functions that interact with the database via Drizzle ORM, keeping routers focused on HTTP layer concerns.
- Shared utilities like
authenticateApiRequestenforce security consistently across both REST and WebSocket interfaces. - The WebSocket router at
/wshandles real-time connections using the same authentication flow as the main API.
Frequently Asked Questions
What framework powers the Kaneo API?
The Kaneo API is built on Hono, a lightweight, edge-compatible web framework. It uses Hono's router composition features to create a hierarchical structure where the root app mounts sub-routers for the API namespace and individual features.
How does authentication work across all API routes?
Authentication is enforced by the authenticateApiRequest middleware defined in apps/api/src/utils/authenticate-api-request.ts. This middleware is attached to the API router with api.use("*", ...) in apps/api/src/index.ts, ensuring every request—including those to feature routers—validates session tokens or API keys before executing handlers.
Where should new endpoints be added when extending the API?
New features should follow the existing pattern: create a directory under apps/api/src/ (e.g., apps/api/src/feature-name/), add an index.ts that exports a Hono router with your routes, and import this router into apps/api/src/index.ts to mount it via api.route("/feature-name", featureName). Controllers should be placed in a controllers/ subdirectory within your feature folder.
How does the API handle real-time communication?
Real-time functionality is provided by a dedicated WebSocket sub-router in apps/api/src/ws/index.ts. This router handles HTTP upgrade requests at /ws/* paths and uses the same authenticateApiRequest middleware to secure connections before establishing the WebSocket channel, ensuring parity with the REST API's security model.
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 →