# Dashboard App Example This example demonstrates a full-stack composable monorepo project using rdev's enhanced templates with: - **Chassis patterns**: Wrap, Bind, HTTPError, Health probes - **Design system**: packages/ui, packages/layout - **Authentication**: pkg/auth, packages/auth - **OpenAPI**: Auto-generated TypeScript client - **Next.js 14**: App Router with server actions ## Creating This Example ```bash # 1. Create the project curl -X POST "$RDEV_API_URL/project" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "dashboard-app", "description": "Example dashboard application" }' # 2. Add the API service curl -X POST "$RDEV_API_URL/projects/dashboard-app/components" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "service", "name": "api" }' # 3. Add the Next.js dashboard curl -X POST "$RDEV_API_URL/projects/dashboard-app/components" \ -H "X-API-Key: $RDEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "app-nextjs", "name": "dashboard" }' ``` ## Project Structure After creation, the project looks like: ``` dashboard-app/ ├── CLAUDE.md # AI routing with guides ├── README.md # Project docs ├── go.work # Go workspace ├── pnpm-workspace.yaml # pnpm monorepo config ├── .woodpecker.yml # CI pipeline │ ├── pkg/ # Shared Go packages │ ├── app/ # Wrap, Bind, Health │ │ ├── app.go # NewApp, Run │ │ ├── handler.go # Wrap pattern │ │ ├── bind.go # BindAndValidate │ │ └── health.go # Health probes │ ├── httperror/ # Typed HTTP errors │ │ └── error.go # Sentinels, factory functions │ ├── httpresponse/ # JSON envelope │ ├── httpvalidation/ # Validator wrapper │ ├── middleware/ # HTTP middleware │ └── auth/ # JWT + API key auth │ ├── auth.go # Interface, types │ ├── context.go # GetUser, SetUser │ ├── jwt.go # JWTValidator │ ├── apikey.go # APIKeyValidator │ └── middleware.go # Auth middleware │ ├── packages/ # Shared TypeScript packages │ ├── ui/ # Design system │ │ ├── src/styles/tokens.css # CSS custom properties │ │ ├── src/utils/cn.ts # clsx + tailwind-merge │ │ └── src/components/ # Button, Card, Input, etc. │ ├── layout/ # Shell components │ │ ├── src/DashboardShell.tsx # Main layout │ │ ├── src/Sidebar.tsx # Navigation │ │ └── src/Header.tsx # Top bar │ ├── auth/ # React auth │ │ ├── src/AuthProvider.tsx # Context provider │ │ ├── src/useAuth.ts # Hook │ │ └── src/ProtectedRoute.tsx # Route guard │ ├── logger/ # Structured logging │ └── api-client/ # Generated client │ └── src/schema.d.ts # TypeScript types │ ├── services/ # Backend services │ └── api/ │ ├── cmd/server/main.go # Entry point │ ├── internal/ │ │ ├── api/routes.go # Chi routes with Wrap │ │ ├── config/config.go # Configuration │ │ └── domain/ # Business models │ ├── migrations/ # SQL migrations │ ├── Dockerfile # Multi-stage Go build │ └── component.yaml # Port, dependencies │ └── apps/ # Frontend applications └── dashboard/ ├── src/app/ │ ├── layout.tsx # Root layout │ ├── globals.css # Design tokens import │ ├── (dashboard)/ # Protected route group │ │ ├── layout.tsx # DashboardShell │ │ ├── page.tsx # Home │ │ └── settings/page.tsx │ └── login/page.tsx # Public login ├── src/components/ │ └── providers.tsx # AuthProvider wrapper ├── src/actions/ # Server actions ├── Dockerfile # Next.js standalone └── component.yaml ``` ## Development Workflow ### Local Development ```bash # Start infrastructure (Postgres, Redis) docker-compose up -d # Install dependencies ./scripts/install.sh # Start all services ./scripts/dev.sh ``` ### Building a Feature 1. **Create feature branch** ```bash git checkout -b feature/user-profile ``` 2. **Add API endpoint** using Wrap + Bind: ```go func (h *Handler) GetMe() http.HandlerFunc { return app.Wrap(func(w http.ResponseWriter, r *http.Request) error { user, ok := auth.GetUser(r.Context()) if !ok { return httperror.Unauthorized("not authenticated") } profile, err := h.svc.GetByID(r.Context(), user.ID) if err != nil { return httperror.WrapError(err, "failed to get profile") } httpresponse.JSON(w, http.StatusOK, profile) return nil }) } ``` 3. **Regenerate TypeScript client**: ```bash ./scripts/generate-client.sh ``` 4. **Add frontend page** using design system: ```tsx import { Card, CardHeader, CardTitle } from '@dashboard-app/ui'; import { useAuth } from '@dashboard-app/auth'; export default function ProfilePage() { const { user } = useAuth(); return ( Profile: {user?.name} ); } ``` 5. **Run tests and quality checks**: ```bash ./scripts/quality.sh ``` 6. **Commit and push**: ```bash git add -A git commit -m "feat: add user profile" git push -u origin feature/user-profile ``` ## Key Patterns ### Backend: Wrap Pattern ```go // Error-returning handlers become http.HandlerFunc app.Wrap(func(w http.ResponseWriter, r *http.Request) error { // Return errors - they become proper HTTP responses if err := doThing(); err != nil { return httperror.Internal("something went wrong") } httpresponse.JSON(w, http.StatusOK, data) return nil }) ``` ### Backend: Bind Pattern ```go // Decode + validate in one call var req CreateItemRequest if err := app.BindAndValidate(r, &req); err != nil { return err // Validation errors become 400 with details } ``` ### Frontend: Auth Hook ```tsx const { user, isLoading, login, logout } = useAuth(); if (isLoading) return ; if (!user) return ; ``` ### Frontend: Design System ```tsx import { Button, Card, Input, Badge } from '@dashboard-app/ui'; import { DashboardShell, Sidebar } from '@dashboard-app/layout'; ``` ## Deployment CI/CD is automated via Woodpecker: 1. Push triggers build 2. Kaniko builds Docker images 3. Images pushed to registry 4. kubectl deploys to K8s Access the deployed app: ```bash open https://.threesix.ai ``` ## Cleanup ```bash curl -X DELETE "$RDEV_API_URL/project/dashboard-app" \ -H "X-API-Key: $RDEV_API_KEY" ``` ## Related Documentation - [Feature Development Cookbook](../../cookbooks/feature-development.md) - [Composable App Cookbook](../../cookbooks/composable-app.md) - [API Framework Guide](../../.claude/guides/packages/api-framework.md)