managarten/.claude/guidelines/hono-server.md
Till JS 53b3746b98 refactor: rename nutriphi module to food (Essen)
Complete rename across the entire monorepo pre-launch:
- Module, routes, API, i18n, standalone landing app directories
- All code identifiers, display names, logo component
- German user-facing label: "Essen" (English brand stays "Food")
- Dexie table nutriFavorites -> foodFavorites
- Infra configs (docker-compose, cloudflared, nginx, wrangler)

Zero residue of nutriphi remains. No data migration needed (pre-launch).

Follow-up: run pnpm install, update Cloudflare DNS
(food.mana.how), rename Cloudflare Pages project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:30:07 +02:00

4.5 KiB

Hono Server Guidelines

Overview

All app compute servers use Hono + Bun with a lightweight architecture. Servers handle only what can't run client-side: file uploads (S3), AI calls, RRULE expansion, external API integration, etc. All CRUD is handled client-side via local-first (Dexie.js + mana-sync).

Project Structure

apps/{project}/apps/server/
├── src/
│   ├── index.ts               # Entry point + route mounting
│   ├── routes/                # Route handlers
│   │   ├── {feature}.ts
│   │   └── admin.ts
│   ├── lib/                   # Shared utilities
│   └── db/                    # Drizzle schema (if needed)
│       ├── schema/
│       ├── connection.ts
│       └── drizzle.config.ts
├── package.json
└── tsconfig.json

Entry Point (index.ts)

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { authMiddleware, healthRoute, errorHandler, notFoundHandler } from '@manacore/shared-hono';

const PORT = parseInt(process.env.PORT || '3031', 10);
const CORS_ORIGINS = (process.env.CORS_ORIGINS || 'http://localhost:5173').split(',');

const app = new Hono();

// Standard middleware stack
app.onError(errorHandler);
app.notFound(notFoundHandler);
app.use('*', cors({ origin: CORS_ORIGINS, credentials: true }));
app.route('/health', healthRoute('my-server'));
app.use('/api/*', authMiddleware());

// Routes
app.route('/api/v1/compute', computeRoutes);

export default { port: PORT, fetch: app.fetch };

Shared Hono Package

Use @manacore/shared-hono for consistent middleware across all servers:

import {
	authMiddleware, // JWT validation via mana-auth JWKS
	healthRoute, // Standard /health endpoint
	errorHandler, // Global error handler with logging
	notFoundHandler, // 404 handler
} from '@manacore/shared-hono';

Route Handlers

// src/routes/upload.ts
import { Hono } from 'hono';

export const uploadRoutes = new Hono();

uploadRoutes.post('/avatar', async (c) => {
	const userId = c.get('userId'); // Set by authMiddleware
	const formData = await c.req.formData();
	const file = formData.get('file') as File | null;

	if (!file) return c.json({ error: 'No file' }, 400);
	if (file.size > 5 * 1024 * 1024) return c.json({ error: 'Max 5MB' }, 400);

	// ... handle upload
	return c.json({ url: result.url }, 201);
});

Database (Optional)

Only servers that need their own database use Drizzle. Most apps rely on mana-sync for data persistence.

Servers with Drizzle: chat, todo, moodlit, context, plants, presi, traces, uload, wisekeep, news

Servers without Drizzle (mana-sync only): calendar, contacts, cards, mukke, food, picture, questions, storage

Running Servers

# Development (with watch)
cd apps/{project}/apps/server && bun run --watch src/index.ts

# Via root scripts
pnpm dev:{project}:server    # Just the server
pnpm dev:{project}:local     # sync + server + web (no auth needed)
pnpm dev:{project}:full      # auth + sync + server + web

When to Add a Server

Add a Hono server when the app needs:

  • File operations: S3 uploads, image processing
  • AI/LLM calls: Gemini, OpenAI, etc. (API keys can't be client-side)
  • External APIs: Google Calendar sync, vCard parsing, etc.
  • Heavy compute: RRULE expansion, PDF generation, etc.
  • Admin endpoints: GDPR compliance, data export

Do NOT add a server for pure CRUD — use local-first + mana-sync instead.

Environment Variables

Servers read .env from their directory (Bun auto-loads it):

PORT=3031
CORS_ORIGINS=http://localhost:5173,http://localhost:5188
MANA_CORE_AUTH_URL=http://localhost:3001
DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/myapp  # if Drizzle

Key Differences from Old NestJS Pattern

Aspect Old (NestJS) New (Hono + Bun)
Runtime Node.js Bun
Framework NestJS (decorators, DI) Hono (functional, minimal)
CRUD Server handles all CRUD Client-side (local-first + mana-sync)
Server role Full backend Compute-only endpoints
Auth NestJS guards @manacore/shared-hono middleware
Startup nest start --watch bun run --watch src/index.ts
Config ConfigModule + DI process.env directly