Markdown Converter
Agent skill for markdown-converter
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sign in to like and favorite skills
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is an Nx monorepo combining a Next.js 15 frontend (deployed to Vercel) with a Hono.js backend (deployed to Cloudflare Workers) using Cloudflare D1 (SQLite) database. The project uses pnpm as the package manager.
# Both frontend and backend in parallel pnpm dev # Frontend only (http://localhost:3000) pnpm dev:frontend # Backend only (http://localhost:8787) pnpm dev:backend
# All applications pnpm build # Frontend only pnpm build:frontend
# Run tests on all projects pnpm test # Run linters on all projects pnpm lint
# Create D1 database (outputs database_id for wrangler.toml) pnpm db:create # Generate migrations from Drizzle schema (after schema changes) pnpm db:generate # Run migrations on local database (for development) pnpm db:migrate:local # Run migrations on production database pnpm db:migrate
# Deploy backend to Cloudflare Workers pnpm deploy:backend
Backend (Hono.js on Cloudflare Workers)
@hono/zod-openapi for automatic API documentationdrizzle-orm/d1) for type-safe database operations@hono/zod-openapi for request/response validationBindings type (apps/backend/src/types/bindings.ts)c.env.DB, initialized with createDbClient(c.env.DB)apps/backend/src/routes/ directoryapps/backend/src/db/schema.tsFrontend (Next.js)
ApiClient) for backend communication (apps/frontend/src/lib/api-client.ts)NEXT_PUBLIC_API_URL environment variable (defaults to http://localhost:8787)@shared/types packageShared Types Package
packages/shared-types@shared/types in both frontend and backendThe D1 database uses Drizzle ORM with Drizzle Kit for migrations:
apps/backend/src/db/schema.ts (source of truth)apps/backend/drizzle/migrations/ (auto-generated from schema)apps/backend/src/db/client.ts (typed D1 client)Current tables:
items table - id, name, description, timestampsfruits table - id, name, price, quantity, timestampsWhen modifying schema:
apps/backend/src/db/schema.ts (Drizzle schema - source of truth)pnpm db:generate to auto-generate SQL migrations in drizzle/migrations/pnpm db:migrate:local for local dev or pnpm db:migrate for productionpackages/shared-types/src/index.ts if neededFrontend (.env.local)
NEXT_PUBLIC_API_URL: Backend API URL (required for production, defaults to localhost:8787)Backend (wrangler.toml)
pnpm db:create:
[[d1_databases]] binding = "DB" database_name = "backend-db" database_id = "your-database-id-from-create-command"
Backend (.dev.vars) - git-ignored file for local secrets
apps/backend/project.jsonmain (configured in nx.json)z from @hono/zod-openapicreateRoute() with OpenAPI metadataapp.openapi(route, handler)apps/backend/src/routes/ and export the appapps/backend/src/index.ts using app.route('/', yourRoutes)packages/shared-types/src/index.tsApiClient class in apps/frontend/src/lib/api-client.tsExample pattern (see
apps/backend/src/routes/fruits.ts for reference)
apps/backend/src/db/schema.ts (Drizzle schema)pnpm db:generate to auto-generate SQL migration filesapps/backend/drizzle/migrations/pnpm db:migrate:local to apply to local databasepackages/shared-types/src/index.ts if neededpnpm db:migrate after deploymentNote: Drizzle Kit automatically generates SQL migrations from your TypeScript schema changes. The generated migrations are stored in
apps/backend/drizzle/migrations/ and applied via Wrangler's D1 migration system.
The backend runs on http://localhost:8787 with these endpoints:
Documentation:
GET /docs - Swagger UI (interactive API documentation)GET /docs/openapi.json - OpenAPI specificationHealth:
GET / - Basic health checkGET /api/health - Detailed health status (includes database connection)Items (legacy):
GET /api/items - List all itemsPOST /api/items - Create a new itemDELETE /api/items/:id - Delete an itemFruits (example CRUD with Drizzle + Zod):
GET /api/fruits - List all fruitsGET /api/fruits/:id - Get single fruitPOST /api/fruits - Create fruit (validated)PUT /api/fruits/:id - Update fruit (validated)DELETE /api/fruits/:id - Delete fruitFrontend: Connect repository to Vercel with these settings:
pnpm nx build frontenddist/apps/frontend/.nextpnpm installNEXT_PUBLIC_API_URL (set to Cloudflare Worker URL)Backend: Run
pnpm deploy:backend (requires Cloudflare Wrangler authentication)
The backend includes comprehensive observability features for monitoring, debugging, and performance tracking.
All logs are output in JSON format with consistent structure, making them easy to query and analyze in Cloudflare Dashboard.
Log Levels: DEBUG, INFO, WARN, ERROR
Automatic Logging:
Development (Real-time):
# Stream logs from your deployed Worker wrangler tail # Stream logs from local dev server pnpm dev:backend # Logs appear in terminal
Production (Cloudflare Dashboard):
import { createLogger } from '../middleware/logging'; import { createDbLogger } from '../utils/db-logger'; app.openapi(yourRoute, async (c) => { const logger = createLogger(c); const dbLogger = createDbLogger(logger); // Log info messages logger.info('Processing request', { customData: 'value' }); // Log database queries with performance tracking const results = await dbLogger.logQuery('SELECT', 'tableName', async () => db.select().from(table) ); // Log warnings logger.warn('Something unexpected', { details: 'info' }); // Log errors with stack traces try { // ... code } catch (error) { logger.error('Operation failed', error, { context: 'additional info' }); throw error; } });
Every log entry includes:
timestamp: ISO 8601 timestamplevel: Log level (DEBUG, INFO, WARN, ERROR)message: Human-readable messagerequestId: Unique ID for the request (useful for tracing)userId: Authenticated user ID (when available)metadata: Custom key-value pairserror: Error details with stack trace (for ERROR level)Example log output:
{ "timestamp": "2025-10-19T12:34:56.789Z", "level": "INFO", "message": "Database query executed", "requestId": "1729340096789-abc123def", "userId": "user_123abc", "metadata": { "operation": "SELECT", "table": "images", "duration": 42 } }
The following middleware is configured in
apps/backend/src/index.ts:
DbLoggerFor production deployments, consider enabling:
Workers Logpush (Paid plan - $5/month):
[observability] enabled = true
Analytics Engine (for custom metrics):
Use structured logging: Always pass metadata as objects, not in message strings
// Good logger.info('User created', { userId: user.id, email: user.email }); // Avoid logger.info(`User ${user.id} created with email ${user.email}`);
Include context: Add relevant IDs and metadata to help with debugging
Log at appropriate levels:
Don't log sensitive data: Avoid logging passwords, tokens, or PII