{"id":18826475,"url":"https://github.com/kelindar/folio","last_synced_at":"2025-04-14T01:51:19.801Z","repository":{"id":258485439,"uuid":"854158920","full_name":"kelindar/folio","owner":"kelindar","description":"JSON document-based CMS, written with Go, SQLite \u0026 HTMX","archived":false,"fork":false,"pushed_at":"2024-11-08T14:49:39.000Z","size":6308,"stargazers_count":10,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-11T17:55:56.556Z","etag":null,"topics":["cms","htmx","sqlite"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kelindar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["kelindar"]}},"created_at":"2024-09-08T14:32:48.000Z","updated_at":"2025-02-01T19:02:38.000Z","dependencies_parsed_at":"2025-01-02T02:40:20.546Z","dependency_job_id":"23ac0b70-2169-4b9d-a252-c802829fe9de","html_url":"https://github.com/kelindar/folio","commit_stats":null,"previous_names":["kelindar/folio"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelindar%2Ffolio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelindar%2Ffolio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelindar%2Ffolio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelindar%2Ffolio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kelindar","download_url":"https://codeload.github.com/kelindar/folio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248809024,"owners_count":21164895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cms","htmx","sqlite"],"created_at":"2024-11-08T01:06:06.278Z","updated_at":"2025-04-14T01:51:19.781Z","avatar_url":"https://github.com/kelindar.png","language":"Go","readme":"\u003cp align=\"center\"\u003e\n\u003cimg width=\"330\" height=\"110\" src=\".github/logo.png\" border=\"0\" alt=\"kelindar/folio\"\u003e\n\u003cbr\u003e\n\u003cimg src=\"https://img.shields.io/github/go-mod/go-version/kelindar/folio\" alt=\"Go Version\"\u003e\n\u003ca href=\"https://pkg.go.dev/github.com/kelindar/folio\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/kelindar/folio\" alt=\"PkgGoDev\"\u003e\u003c/a\u003e\n\u003ca href=\"https://goreportcard.com/report/github.com/kelindar/folio\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/kelindar/folio\" alt=\"Go Report Card\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Folio: Effortless Internal Tool Development\n\nFolio is a tool I built to save time on the boring stuff. After years of building internal tools and getting stuck on UI work, I created Folio to automatically generate UIs from Go structs, letting me skip the front-end hassle and focus on the fun parts of development.\n\nIn this example, we define a `Person` struct with various fields and tags for validation and rendering. Folio automatically generates a user interface for the `Person` model, allowing users to create, read, update, and delete records.\n\n```go\ntype Person struct {\n    folio.Meta `kind:\"person\" json:\",inline\"`\n    Name       string    `json:\"name\" form:\"rw\" is:\"required\"`\n    Age        int       `json:\"age\" form:\"rw\" is:\"range(0|130)\"`\n    Gender     string    `json:\"gender\" form:\"rw\" is:\"required,in(male|female|prefer_not_to)\"`\n    Country    string    `json:\"country\" form:\"rw\"`\n    Address    string    `json:\"address\" form:\"rw\"`\n    Phone      string    `json:\"phone\" form:\"rw\"`\n    Boss       folio.URN `json:\"boss\" form:\"rw\" kind:\"person\"`\n    IsEmployed bool      `json:\"isEmployed\" form:\"rw\" desc:\"Is the person employed?\"`\n    JobTitle   string    `json:\"jobTitle\" form:\"rw\"`\n    Workplace  folio.URN `json:\"workplace\" form:\"rw\" kind:\"company\" query:\"namespace=*;match=Inc\"`\n}\n```\n\nThe generated UI includes form fields for each struct field, as well as buttons for creating, updating, and deleting records. The UI also supports pagination, sorting, and filtering.\n\n![demo](./.github/demo.gif)\n\n## Introduction\n\nI’ve built a lot of internal tools over the years — everything from experimentation platforms to machine learning management tools. And while those tools were powerful, the process often felt like a never-ending cycle of reinventing the wheel, except this wheel was for a car that I didn’t really want to drive.\n\nThe problem? The minor stuff always took way more time and energy than it should. Need a UI for CRUD operations? That’ll be hours of React, CSS, and front-end misery. I just wanted to get things done, not spend my weekends pretending to enjoy writing JavaScript.\n\nThat’s where this project comes in. I built this for my personal projects where I have no team, no budget, and let’s be honest — no patience for building full-blown React apps. Folio generates the UI for me straight from my Go structs (view models), so I can focus on the fun parts (or at least the parts that don’t make me want to quit tech and become a beekeeper).\n\nIn short: Folio takes care of the boring stuff, so you can keep your focus on the good stuff—like actually building cool things instead of wrangling with endless form fields and dropdowns.\n\nKeep in mind that this project is still in its early stages, so there’s a lot of room for improvement. I'm also not going to pretend that this is the best solution for every project, and there's still a ton of features that I want to add, so use it at your own risk.\n\n## 🚀 Features\n\n- **Auto-Generated UI**: Automatically generates user interfaces from view models, eliminating the need for manual UI creation.\n- **CRUD Operations**: Simplifies Create, Read, Update, and Delete operations for objects.\n- **Error Handling \u0026 Validation**: Provides utilities for error handling and validation.\n- **Templating**: Uses the `templ` package to define and render HTML templates.\n- **Abstracted Storage**: Supports SQLite database operations, but can be extended to other storage solutions.\n- **Pagination, Search and Filtering**: Built-in support for paginated lists, search, and namespace (i.e project) filtering.\n\n## 🛠 Getting Started\n\n1. Navigate to the company example directory:\n\n   ```sh\n   cd examples/company \u0026\u0026  go run .\n   ```\n\n2. Open your browser and navigate to `http://localhost:7000`.\n\n### 📚 Usage\n\n#### Defining Models\n\nDefine your models by embedding `folio.Meta` and specifying field tags for validation and form rendering.\n\n```go\ntype Person struct {\n    folio.Meta `kind:\"person\" json:\",inline\"`\n    Name       string    `json:\"name\" form:\"rw\" is:\"required\"`\n    Age        int       `json:\"age\" form:\"rw\" is:\"range(0|130)\"`\n    Gender     string    `json:\"gender\" form:\"rw\" is:\"required,in(male|female|prefer_not_to)\"`\n    Country    string    `json:\"country\" form:\"rw\"`\n    Address    string    `json:\"address\" form:\"rw\"`\n    Phone      string    `json:\"phone\" form:\"rw\"`\n    Boss       folio.URN `json:\"boss\" form:\"rw\" kind:\"person\"`\n    IsEmployed bool      `json:\"isEmployed\" form:\"rw\" desc:\"Is the person employed?\"`\n    JobTitle   string    `json:\"jobTitle\" form:\"rw\"`\n    Workplace  folio.URN `json:\"workplace\" form:\"rw\" kind:\"company\" query:\"namespace=*;match=Inc\"`\n}\n```\n\n#### Registering Models\n\nRegister your models with the registry and provide options like icons, titles, and sorting.\n\n```go\nreg := folio.NewRegistry()\nfolio.Register[*Person](reg, folio.Options{\n    Icon:   \"user-round\",\n    Title:  \"Person\",\n    Plural: \"People\",\n    Sort:   \"1\",\n})\n```\n\n#### Starting the Server\n\nUse the render.ListenAndServe function to start the server.\n\n```go\ndb, err := sqlite.Open(\"file:data.db?_journal_mode=WAL\", reg)\nif err != nil {\n    panic(err)\n}\n\nif err := render.ListenAndServe(7000, reg, db); err != nil {\n    slog.Error(\"Failed to start server!\", \"details\", err.Error())\n    os.Exit(1)\n}\n```\n\n#### Contributing\n\nContributions are welcome! Please open an issue or submit a pull request on GitHub.\n\n#### License\n\nThis project is licensed under the MIT License. See the LICENSE file for details.\n\n#### Acknowledgements\n\nThis project leverages several open-source libraries and tools. We would like to acknowledge and thank the following projects:\n\n- **templ**: A Go package for defining and rendering HTML templates.\n- **htmx-go**: A Go package for integrating htmx with Go web applications.\n- **SQLite**: A C library that provides a lightweight, disk-based database.\n- **Franken-UI**: A collection of UI components for building modern web applications.\n- **Tailwind CSS**: A utility-first CSS framework for rapidly building custom user interfaces.\n","funding_links":["https://github.com/sponsors/kelindar"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkelindar%2Ffolio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkelindar%2Ffolio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkelindar%2Ffolio/lists"}