- Update version format to YYYY.MM.PPP (zero-padded) - Current version: 2026.01.002 - Document version management policy in CLAUDE.md - Version increments with every production deployment - Patch resets to 001 when month changes
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Basil is a full-stack recipe manager built with TypeScript. It features a REST API backend, React web frontend, and PostgreSQL database. The application supports importing recipes from URLs via schema.org markup parsing, local/S3 image storage, and comprehensive recipe management (CRUD operations, search, tagging).
Architecture
Monorepo Structure:
packages/shared/- Shared TypeScript types and interfaces used across all packagespackages/api/- Express.js REST API server with Prisma ORMpackages/web/- React + Vite web application- Future:
packages/mobile/- React Native mobile apps
Key Technologies:
- Backend: Node.js, TypeScript, Express, Prisma ORM, PostgreSQL
- Frontend: React, TypeScript, Vite, React Router
- Infrastructure: Docker, Docker Compose, nginx
- Recipe Scraping: Cheerio for HTML parsing, Schema.org Recipe markup support
Database Schema:
Recipe- Main recipe entity with metadata (prep/cook time, servings, ratings)Ingredient- Recipe ingredients with amounts, units, and orderingInstruction- Step-by-step instructions with optional imagesRecipeImage- Multiple images per recipeTag/RecipeTag- Many-to-many tagging system
Storage Architecture:
- Primary: Local filesystem storage (
./uploads) - Optional: S3 storage (configurable via environment variables)
- Storage service implements strategy pattern for easy switching
Development Commands
# Install all dependencies (root + all packages)
npm install
# Development mode (starts all packages with hot reload)
npm run dev
# Build all packages
npm run build
# Lint all packages
npm run lint
# Testing
npm test # Run all unit tests
npm run test:e2e # Run E2E tests with Playwright
npm run test:e2e:ui # Run E2E tests with Playwright UI
# Docker commands
npm run docker:up # Start all services (PostgreSQL, API, web)
npm run docker:down # Stop all services
npm run docker:build # Rebuild Docker images
Backend-Specific Commands
cd packages/api
# Development with auto-reload
npm run dev
# Database migrations
npm run prisma:migrate # Create and apply migration
npm run prisma:generate # Generate Prisma client
npm run prisma:studio # Open Prisma Studio GUI
# Build for production
npm run build
npm start
Frontend-Specific Commands
cd packages/web
# Development server
npm run dev
# Production build
npm run build
npm run preview # Preview production build locally
Configuration
Environment Variables (packages/api/.env):
PORT=3001
NODE_ENV=development
DATABASE_URL=postgresql://basil:basil@localhost:5432/basil?schema=public
STORAGE_TYPE=local # or 's3'
LOCAL_STORAGE_PATH=./uploads
BACKUP_PATH=./backups
CORS_ORIGIN=http://localhost:5173
For S3 storage, add:
S3_BUCKET=basil-recipes
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=your-key
S3_SECRET_ACCESS_KEY=your-secret
For remote PostgreSQL database, update:
DATABASE_URL=postgresql://username:password@remote-host:5432/basil?schema=public
Key Features
Recipe Import from URL
POST /api/recipes/import- Scrapes recipe from URL using schema.org markup- Extracts: title, description, ingredients, instructions, times, images
- Handles JSON-LD structured data (primary) with fallback to manual parsing
- Downloads and stores images locally or on S3
Recipe Management
- Full CRUD operations on recipes
- Pagination and search (by title, description)
- Filtering by cuisine, category
- Image upload with multiple images per recipe
- Tagging system for organization
Storage Service
- Abstracted storage interface in
packages/api/src/services/storage.service.ts - Local storage: Saves to filesystem with timestamped filenames
- S3 storage: Placeholder for AWS SDK implementation
- Easy to extend for other storage providers
Backup & Restore
- Complete data backup to single ZIP file including database and uploaded files
- Backup service in
packages/api/src/services/backup.service.ts - REST API for creating, listing, downloading, and restoring backups
- Automatic backup of all recipes, cookbooks, tags, and relationships
- Configurable backup storage location via
BACKUP_PATHenvironment variable
Adding New Features
Adding a New API Endpoint
- Add route handler in
packages/api/src/routes/*.routes.ts - Update shared types in
packages/shared/src/types.tsif needed - Rebuild shared package:
cd packages/shared && npm run build - Use Prisma client for database operations
Adding a New Frontend Page
- Create component in
packages/web/src/pages/ - Add route in
packages/web/src/App.tsx - Create API service methods in
packages/web/src/services/api.ts - Import and use shared types from
@basil/shared
Database Schema Changes
- Edit
packages/api/prisma/schema.prisma - Run
npm run prisma:migrateto create migration - Run
npm run prisma:generateto update Prisma client - Update TypeScript types in
packages/shared/src/types.tsto match
Docker Deployment
The project includes full Docker support for production deployment:
docker-compose up -d
This starts:
- PostgreSQL database (port 5432)
- API server (port 3001)
- Web frontend served via nginx (port 5173)
Persistent volumes:
postgres_data- Database storageuploads_data- Uploaded imagesbackups_data- Backup files
Using a Remote Database
To use a remote PostgreSQL database instead of the local Docker container:
- Set the
DATABASE_URLenvironment variable to point to your remote database - Update
docker-compose.ymlto pass the environment variable or create a.envfile in the root - Optionally, remove or comment out the
postgresservice and its dependency indocker-compose.yml
Example .env file in project root:
DATABASE_URL=postgresql://username:password@remote-host:5432/basil?schema=public
The docker-compose.yml is configured to use ${DATABASE_URL:-default} which will use the environment variable if set, or fall back to the local postgres container.
API Reference
Recipes:
GET /api/recipes- List all recipes (supports pagination, search, filters)GET /api/recipes/:id- Get single recipe with all relationsPOST /api/recipes- Create new recipePUT /api/recipes/:id- Update recipeDELETE /api/recipes/:id- Delete recipe and associated imagesPOST /api/recipes/:id/images- Upload image for recipePOST /api/recipes/import- Import recipe from URL
Query Parameters:
page,limit- Paginationsearch- Search in title/descriptioncuisine,category- Filter by cuisine or category
Backups:
POST /api/backup- Create a new backup (returns backup metadata)GET /api/backup- List all available backupsGET /api/backup/:filename- Download a specific backup filePOST /api/backup/restore- Restore from backup (accepts file upload or existing filename)DELETE /api/backup/:filename- Delete a backup file
Important Implementation Details
Prisma Relations
- All related entities (ingredients, instructions, images, tags) use cascade delete
- Ingredients and instructions maintain ordering via
orderandstepfields - Tags use many-to-many relationship via
RecipeTagjoin table
Recipe Scraping
- Primary: Parses JSON-LD
application/ld+jsonscripts for Schema.org Recipe data - Fallback: Extracts basic info from HTML meta tags and headings
- Handles ISO 8601 duration format (PT30M, PT1H30M) for cook times
- Downloads images asynchronously and stores them locally
Frontend Routing
- Uses React Router v6 for client-side routing
- Vite proxy forwards
/apiand/uploadsrequests to backend during development - Production uses nginx reverse proxy to backend API
TypeScript Workspace
- Root
package.jsondefines npm workspaces - Packages can reference each other (e.g.,
@basil/shared) - Must rebuild shared package when types change for other packages to see updates
CI/CD and Deployment
Basil includes a complete CI/CD pipeline with Gitea Actions for automated testing, building, and deployment.
Quick Start:
- See CI/CD Setup Guide for full documentation
- See Deployment Quick Start for quick reference
Pipeline Overview:
- Test Stage: Runs unit tests (Vitest) and E2E tests (Playwright)
- Build Stage: Builds Docker images for API and Web (main branch only)
- Deploy Stage: Pushes images to registry and triggers webhook deployment
Deployment Options:
- Automatic: Push to main branch triggers full CI/CD pipeline
- Manual: Run
./scripts/manual-deploy.shfor interactive deployment - Webhook: Systemd service listens for deployment triggers
Key Files:
.gitea/workflows/ci-cd.yml- Main CI/CD workflowscripts/deploy.sh- Deployment scriptscripts/webhook-receiver.sh- Webhook server.env.deploy.example- Deployment configuration template
Required Secrets (Gitea):
DOCKER_USERNAME- Docker Hub usernameDOCKER_PASSWORD- Docker Hub access tokenDEPLOY_WEBHOOK_URL- Webhook endpoint for deployments
Version Management
IMPORTANT: Increment the version with every production deployment.
Version Format
Basil uses calendar versioning with the format: YYYY.MM.PPP
YYYY- Four-digit year (e.g., 2026)MM- Two-digit month with zero-padding (e.g., 01 for January, 12 for December)PPP- Three-digit patch number with zero-padding that increases with each deployment in a month
Examples
2026.01.001- First deployment in January 20262026.01.002- Second deployment in January 20262026.02.001- First deployment in February 2026 (patch resets to 001)2026.02.003- Third deployment in February 2026
Version Update Process
When deploying to production:
-
Update version files:
# Update both version files with new version # packages/api/src/version.ts # packages/web/src/version.ts export const APP_VERSION = '2026.01.002'; -
Commit the version bump:
git add packages/api/src/version.ts packages/web/src/version.ts git commit -m "chore: bump version to 2026.01.002" git push origin main -
Create Git tag and release:
# Tag should match version with 'v' prefix git tag v2026.01.002 git push origin v2026.01.002 # Or use Gitea MCP to create tag and release -
Document in release notes:
- Summarize changes since last version
- List bug fixes, features, and breaking changes
- Reference related pull requests or issues
Version Display
The current version is displayed in:
- API:
GET /api/versionendpoint returns{ version: '2026.01.002' } - Web: Footer or about section shows current version
- Both packages export
APP_VERSIONconstant for internal use