# Composable App Cookbook > Deploy a full-stack application with multiple components using composable monorepo templates. ## Overview This cookbook creates a full-stack application by composing components: ``` POST /project ↓ Creates: Monorepo skeleton with shared packages ↓ POST /projects/{id}/components (service) ↓ Adds: Go API backend with CI step ↓ POST /projects/{id}/components (app) ↓ Adds: React/Astro frontend with CI step ↓ Git push triggers Woodpecker CI ↓ CI builds and deploys all components to K8s ``` **Composable. Template-driven. CI auto-configured.** --- ## Prerequisites ### API Access ```bash export RDEV_API_URL="https://rdev.masq-ops.orchard9.ai" export RDEV_API_KEY="" ``` ### Infrastructure Required - rdev-api running with embedded worker - Gitea at https://git.threesix.ai - Woodpecker CI at https://ci.threesix.ai --- ## Step 1: Create Project (Monorepo Skeleton) ```bash curl -X POST "$RDEV_API_URL/project" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "taskapp", "description": "Task management application" }' ``` **Response:** ```json { "data": { "project_id": "taskapp", "name": "taskapp", "domain": "xyz789ab.threesix.ai", "url": "https://xyz789ab.threesix.ai", "git": { "owner": "jordan", "name": "taskapp", "html_url": "https://git.threesix.ai/jordan/taskapp" } } } ``` This creates: ``` taskapp/ ├── CLAUDE.md # AI routing ├── README.md # Project docs ├── Procfile # Local dev (empty) ├── docker-compose.yml # Postgres, Redis ├── go.work # Go workspace ├── .woodpecker.yml # CI pipeline (template-provided) ├── .golangci.yml # Go linting ├── scripts/ # Discovery scripts │ ├── discover.sh │ ├── install.sh │ ├── quality.sh │ └── dev.sh ├── pkg/ # Shared Go packages │ ├── app/ # Service bootstrapper │ ├── middleware/ # HTTP middleware │ ├── httpresponse/ # JSON responses │ └── ... ├── services/ # (empty, ready for components) ├── apps/ # (empty, ready for components) ├── workers/ # (empty, ready for components) └── cli/ # (empty, ready for components) ``` --- ## Step 2: Add Backend Service ```bash curl -X POST "$RDEV_API_URL/projects/taskapp/components" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "service", "name": "api", "template": "service" }' ``` **Response:** ```json { "data": { "type": "service", "name": "api", "path": "services/api", "port": 8001, "template": "service" } } ``` This adds: ``` services/api/ ├── cmd/server/main.go # Entry point using pkg/app ├── internal/ │ ├── api/routes.go # Chi router setup │ ├── api/handlers/health.go # Health endpoints │ └── config/config.go # Configuration ├── migrations/ # Database migrations ├── Makefile # Build targets ├── Dockerfile # Multi-stage Go build ├── component.yaml # Port, dependencies └── .env.example # Environment template ``` And updates: - `.woodpecker.yml` - adds `build-api` step - `Procfile` - adds `api: make -C services/api run` - `go.work` - adds `use ./services/api` --- ## Step 3: Add Frontend App ```bash curl -X POST "$RDEV_API_URL/projects/taskapp/components" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "app-react", "name": "dashboard" }' ``` **Response:** ```json { "data": { "type": "app-react", "name": "dashboard", "path": "apps/dashboard", "port": 3001 } } ``` This adds: ``` apps/dashboard/ ├── src/ │ ├── App.tsx │ ├── main.tsx │ └── components/ ├── package.json ├── vite.config.ts ├── tsconfig.json ├── Dockerfile # Multi-stage Node build └── component.yaml # Port, dependencies ``` --- ## Step 4: Customize with Claude (Optional) Submit a build task to customize the components: ```bash curl -X POST "$RDEV_API_URL/projects/taskapp/builds" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Implement a task management system:\n\nBACKEND (services/api):\n- Add Task model with id, title, description, status, created_at\n- Endpoints: GET /api/tasks, POST /api/tasks, PATCH /api/tasks/{id}, DELETE /api/tasks/{id}\n- In-memory storage for now\n\nFRONTEND (apps/dashboard):\n- Task list with status badges\n- Add task form\n- Mark complete/delete buttons\n- Dark theme with Tailwind\n- Fetch from /api/tasks", "auto_commit": true, "auto_push": true }' ``` Monitor the build: ```bash curl -s "$RDEV_API_URL/builds/{task_id}" \ -H "X-API-Key: $RDEV_API_KEY" | jq '.data.status' ``` --- ## Step 5: Deployment (CI-Driven) Each component's CI step includes a `deploy-{name}` phase that runs `kubectl set image` on merge to `main`. Pushing code triggers the full build-and-deploy pipeline automatically. To manually deploy a single component outside of CI: ```bash # Example: deploy the api service with a specific image tag kubectl set image deployment/taskapp-api \ api=registry.threesix.ai/taskapp/api: \ -n projects ``` --- ## Step 6: Monitor CI Pipeline ```bash curl -s "$RDEV_API_URL/projects/taskapp/pipelines" \ -H "X-API-Key: $RDEV_API_KEY" | jq '.data[0]' ``` The pipeline builds both components in parallel then deploys. --- ## Step 7: Verify Deployment ```bash # Check site is live curl -I https://xyz789ab.threesix.ai # Test API curl https://xyz789ab.threesix.ai/api/tasks | jq . # View the app open https://xyz789ab.threesix.ai ``` --- ## Adding More Components ### Add a Background Worker ```bash curl -X POST "$RDEV_API_URL/projects/taskapp/components" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "worker", "name": "notifications", "template": "worker" }' ``` ### Add a CLI Tool ```bash curl -X POST "$RDEV_API_URL/projects/taskapp/components" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "cli", "name": "taskctl", "template": "cli" }' ``` ### List All Components ```bash curl -s "$RDEV_API_URL/projects/taskapp/components" \ -H "X-API-Key: $RDEV_API_KEY" | jq '.data' ``` --- ## Component Types | Type | Directory | Stack | Default Port | |------|-----------|-------|--------------| | service | `services/` | go | 8001+ | | worker | `workers/` | go | N/A | | app-astro | `apps/` | astro | 3001+ | | app-react | `apps/` | react | 3001+ | | cli | `cli/` | go | N/A | Ports auto-increment as you add components of the same type. --- ## Teardown ```bash curl -X DELETE "$RDEV_API_URL/project/taskapp" \ -H "X-API-Key: $RDEV_API_KEY" ``` Removes: DNS records, K8s deployments, project metadata. Gitea repo preserved. --- ## E2E Test Script Run the full flow: ```bash ./cookbooks/scripts/composable-test.sh run my-test-app ``` Check status: ```bash ./cookbooks/scripts/composable-test.sh status my-test-app ``` Cleanup: ```bash ./cookbooks/scripts/composable-test.sh teardown my-test-app ``` --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Composable Full-Stack Deployment │ │ │ │ POST /project │ │ │ │ │ └──► Creates monorepo skeleton with: │ │ - Shared pkg/ (8 packages) │ │ - .woodpecker.yml (base CI) │ │ - Discovery scripts │ │ │ │ POST /projects/{id}/components (× N) │ │ │ │ │ └──► For each component: │ │ - Renders template to services/|apps/|workers/|cli/ │ │ - Inserts CI step into .woodpecker.yml │ │ - Updates Procfile, go.work │ │ │ │ POST /projects/{id}/builds (optional) │ │ │ │ │ └──► Claude customizes existing components │ │ │ │ Git push → Woodpecker CI: │ │ ├──► build-api (Kaniko → registry) │ │ ├──► deploy-api (kubectl set image) │ │ ├──► build-dashboard (Kaniko → registry) │ │ ├──► deploy-dashboard (kubectl set image) │ │ └──► verify (confirm deployments) │ │ │ │ Components live at https://{slug}.threesix.ai │ │ ├──► /api/* → services/api │ │ └──► /* → apps/dashboard │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## Troubleshooting ### Component addition fails ```bash # Check project exists curl -s "$RDEV_API_URL/project/taskapp" \ -H "X-API-Key: $RDEV_API_KEY" | jq '.data' # Check available component templates curl -s "$RDEV_API_URL/templates/components" \ -H "X-API-Key: $RDEV_API_KEY" | jq '.data.components' ``` ### Build stuck in pending ```bash # Check worker status curl -s "$RDEV_API_URL/workers" -H "X-API-Key: $RDEV_API_KEY" | jq '.data.summary' ``` ### Pipeline fails ```bash # Get pipeline details curl -s "$RDEV_API_URL/projects/taskapp/pipelines/1" \ -H "X-API-Key: $RDEV_API_KEY" | jq '.data' # Check Woodpecker UI open https://ci.threesix.ai/jordan/taskapp ``` ### Deployment not updating ```bash # Check K8s pods kubectl get pods -n projects -l app=taskapp # Check deployment events kubectl describe deployment taskapp-api -n projects ``` --- ## Related - [Landing Page Cookbook](./landing-page.md) - Simple single-component site - [Composable Monorepo Guide](../.claude/guides/services/composable-monorepo.md) - [Component Templates](../.claude/guides/services/templates.md)