Skip to content

Backend Architecture & Hono RPC

The Kryptofolio backend (apps/backend) is a centralized, lightweight, and ultra-fast service built on Hono.js.

It acts as the single source of truth for:

  1. REST API endpoints consumed by the Vue 3 frontend.
  2. End-to-end type safety via Hono RPC without code-generation steps.
  3. Encrypted API credentials storage (Vault).
  4. Analytical execution coordination between SQLite and DuckDB.

The backend is cleanly separated into two distinct entry points:

apps/backend/src/
β”œβ”€β”€ app.ts # Pure Hono app: middlewares, routing, and AppType export
β”œβ”€β”€ index.ts # Bootstrapper: DB connection pooling, job triggers, port binding
β”œβ”€β”€ core/
β”‚ β”œβ”€β”€ domain/ # Domain models and interfaces
β”‚ β”œβ”€β”€ application/ # Use Cases (e.g. UnlockVaultUseCase, IngestTransactionsUseCase)
β”‚ └── infrastructure/
β”‚ β”œβ”€β”€ di/container.ts# Centralized Dependency Injection Composition Root
β”‚ β”œβ”€β”€ adapters/ # SQLite & DuckDB engine adapters
β”‚ β”œβ”€β”€ routes/ # Modular Hono route definitions
β”‚ └── jobs/ # Scheduled background tasks (e.g. ExchangeRateSyncJob)
  • app.ts (Pure Application): Defines route paths, sets up CORS, logger middlewares, and exports the AppType. It contains no network listening logic, making it effortlessly testable with app.request().
  • index.ts (Runtime Bootstrapper): Initializes database files, runs schema migrations, verifies environment variables, and starts the Node.js HTTP server.

Traditional architectures often require running heavy OpenAPI/Swagger code generators (like openapi-typescript-codegen or GraphQL codegen) whenever an endpoint changes.

Kryptofolio leverages Hono RPC, sharing TypeScript types directly across monorepo boundaries with zero build-step overhead:

1. Backend Route Definition & Export (apps/backend/src/app.ts)

Section titled β€œ1. Backend Route Definition & Export (apps/backend/src/app.ts)”
import { Hono } from 'hono';
import { metricsRoutes } from './core/infrastructure/routes/metrics';
import { taxRoutes } from './core/infrastructure/routes/tax';
import { vaultRoutes } from './core/infrastructure/routes/vault';
const routes = new Hono()
.route('/api/metrics', metricsRoutes)
.route('/api/tax', taxRoutes)
.route('/api/credentials', vaultRoutes);
export const app = new Hono().route('/', routes);
export type AppType = typeof routes;

2. Frontend Consumption (apps/frontend/.../BffClient.ts)

Section titled β€œ2. Frontend Consumption (apps/frontend/.../BffClient.ts)”

The frontend initializes the RPC client importing only the AppType type definition:

import { hc } from 'hono/client';
import type { AppType } from '@kryptofolio/backend';
export const bffClient = hc<AppType>(import.meta.env.VITE_API_URL || 'http://localhost:3001');
// Full autocomplete, argument validation, and return typing:
const response = await bffClient.api.metrics.kpis.$get();
if (response.ok) {
const data = await response.json(); // Data is strictly typed!
console.log(data.total_equity);
}

πŸ”Œ Centralized Dependency Injection (container.ts)

Section titled β€œπŸ”Œ Centralized Dependency Injection (container.ts)”

The backend routes do not instantiate services directly. A centralized DI container resolves concrete adapters and injects them into Use Cases:

apps/backend/src/core/infrastructure/di/container.ts
export interface Container {
sqliteLedgerAdapter: SqliteLedgerAdapter;
duckDbAdapter: DuckDbAdapter;
fifoMaterializerService: FifoMaterializerService;
vaultCredentialsPort: IVaultCredentialsPort;
ingestAndMaterializeUseCase: IngestAndMaterializeUseCase;
}

This guarantees:

  • Testability: In test environments, the container easily swaps file-backed databases for :memory: instances.
  • Single Instance Pooling: DuckDB connections and SQLite handles are properly pooled and shared without leaks.

Route Prefix Method Purpose
/api/metrics/kpis GET Portfolio summary (equity, cost basis, realized/unrealized PnL, win rate).
/api/metrics/risk GET Institutional metrics (Sharpe ratio, 30d volatility, ATH drawdown, Alpha, Beta).
/api/metrics/performance GET Daily valuation time-series in the requested currency (default EUR), converted per point at that date’s rate; points with no rate are null.
/api/tax/import POST Ingestion pipeline submission with source format profile.
/api/tax/lots GET Query calculated tax lots with remaining balances and custody provenance.
/api/settings/accounts GET Lists the user-facing accounts (id as value, name as label); synthetic custody counterparties are excluded by ListAccountsUseCase.
/api/tax/transactions/spot GET Spot transactions for the effective (edited) view, optionally filtered by accountId, through SearchSpotTransactionsUseCase. Ledger order, oldest first.
/api/credentials/vault/* Multiple Manage encrypted third-party credentials and vault lock state.
/api/advisor/* Multiple Read-only AI Portfolio Advisor: streaming chat, model chain and execution-profile configuration. See AI Advisor.