# Congo Project Built with the Congo framework — Go + HTMX + DaisyUI + React islands. ## Commands ```bash congo dev # development server (ENV=development, in-memory DB) congo build # production binary congo launch # deploy to remote server congo new # add a new app to the project congo connect # SSH into deployed server congo destroy # tear down infrastructure congo claude # AI-assisted development congo status # check deployment health congo logs # stream service logs congo source # extract flat buildable copy of framework ``` ## Architecture MVC with HTMX-first server rendering. React islands for complex interactive UI. ``` controllers/ Route handlers + template methods models/ Database models with auto-migration ORM views/ Go HTML templates (HTMX + DaisyUI) components/ React island components (optional) internal/ Vendored Congo framework (modifiable) ``` ## Controller Pattern ```go // Factory function returns (name, controller). func Todos() (string, *TodosController) { return "todos", &TodosController{} } type TodosController struct { application.BaseController // Always embed } func (c *TodosController) Setup(app *application.App) { c.BaseController.Setup(app) http.Handle("GET /todos", app.Serve("todos.html", nil)) http.Handle("POST /todos", app.Method(c, "Create", nil)) http.Handle("POST /todos/{id}/delete", app.Method(c, "Delete", nil)) } // VALUE receiver — creates copy for request isolation. func (c TodosController) Handle(r *http.Request) application.Controller { c.Request = r return &c } // Template methods (pointer receiver) — accessible as {{todos.All}}. func (c *TodosController) All() []*models.Todo { todos, _ := models.Todos.Search("ORDER BY CreatedAt DESC") return todos } // POST handler — create. func (c *TodosController) Create(w http.ResponseWriter, r *http.Request) { todo := &models.Todo{Title: r.FormValue("title")} if _, err := models.Todos.Insert(todo); err != nil { c.RenderError(w, r, err) return } c.Refresh(w, r) // reload current page } // POST handler — delete. func (c *TodosController) Delete(w http.ResponseWriter, r *http.Request) { if err := models.Todos.DeleteByID(r.PathValue("id")); err != nil { c.RenderError(w, r, err) return } c.Redirect(w, r, "/todos") // navigate to different URL } ``` Register in `main.go`: ```go application.WithController(controllers.Todos()), ``` ## Model Pattern ```go type Todo struct { database.Model // ID (string UUID), CreatedAt, UpdatedAt Title string Done bool } var Todos = database.Manage(DB, new(Todo), database.WithIndex[Todo]("Title"), ) ``` Database init in `models/db.go`: ```go var DB = engines.NewAuto() // env-based: DB_URL+DB_TOKEN=remote, DB_PATH=local, neither=memory ``` Collection methods: `Get(id)`, `First(where, args...)`, `Search(where, args...)`, `All()`, `Insert(entity)`, `Update(entity)`, `Delete(entity)`, `DeleteByID(id)`, `Count(where, args...)`. Query examples: ```go models.Todos.Search("WHERE Done = ? ORDER BY CreatedAt DESC LIMIT 10", false) models.Todos.First("WHERE Title = ?", "Buy milk") models.Todos.Count("WHERE Done = ?", true) ``` ## View + HTMX Pattern ```html {{template "main.html" .}} {{define "title"}}Todos{{end}} {{define "content"}}
{{range $todo := todos.All}}
{{$todo.Title}}
{{end}}
{{end}} ``` ## Key Conventions - **IDs are ALWAYS strings** (UUIDs). Never convert to int. - **SQL uses PascalCase**: `WHERE UserID = ?` not `WHERE user_id = ?` - **Templates by filename only**: `{{template "nav.html" .}}` not `"partials/nav.html"` - **Render partial**: `c.Render(w, r, "partial.html", data)` in POST handlers - **Error handling**: `c.RenderError(w, r, err)` returns 200 OK with error HTML for HTMX - **Redirect**: `c.Redirect(w, r, "/path")` — sends HX-Location header for HTMX, 303 for regular requests - **Refresh**: `c.Refresh(w, r)` — sends HX-Refresh header for HTMX, 303 redirect-to-self for regular requests - **CSRF**: HTMX + SameSite=Lax cookies provides CSRF protection. No tokens needed. - **PathValue**: in handlers `r.PathValue("id")`, in template methods `c.PathValue("id")` ## Application Options ```go application.WithValue("key", value) // template function returning value application.WithMiddleware(mw) // add middleware to handler chain application.WithHealthPath("/healthz") // custom health endpoint (default: /health) application.WithController(controllers.X()) // register controller application.WithFunc("name", fn) // register template function ``` ## Go Style - Package-level vars as runtime state (no config structs) - `cmp.Or()` for env var defaults (Go 1.22+) - No custom ServeMux — use `http.DefaultServeMux` - No single-line wrapper methods around stdlib calls - Functions exist for reuse or naming — inline single-use logic ## Frontend Islands (when present) React components in `components/`, mounted via `{{render "ComponentName" props}}` in templates. Auto-mounted on page load and HTMX swaps. HMR in development mode. ```html {{render "Counter" home.CounterProps}} ``` Controller provides props: ```go func (c *HomeController) CounterProps() map[string]any { return map[string]any{"initial": 0, "label": "Clicks"} } ```