{"id":20621437,"url":"https://github.com/emarifer/go-frameworkless-htmx-todoapp","last_synced_at":"2025-04-15T12:15:31.451Z","repository":{"id":249000564,"uuid":"830012105","full_name":"emarifer/go-frameworkless-htmx-todoapp","owner":"emarifer","description":"Full stack Demo app made in frameworkless Go (Todo App 🧬), centralized HTTP error handling, CRUD to a SQLite database and HTMx-powered frontend.","archived":false,"fork":false,"pushed_at":"2024-10-16T18:06:25.000Z","size":668,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-15T12:15:02.979Z","etag":null,"topics":["authentication-middleware","centralized-logging","daisyui","error-handling","error-middleware","flash-messages","frameworkless","golang","hateoas","htmx","hypermedia","hyperscript","jwt-authentication","layered-architecture","onion-architecture","server-side-rendering","sweetalert2","tailwindcss","template-engine"],"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/emarifer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2024-07-17T12:41:27.000Z","updated_at":"2024-12-31T10:54:14.000Z","dependencies_parsed_at":"2024-10-18T07:45:46.144Z","dependency_job_id":null,"html_url":"https://github.com/emarifer/go-frameworkless-htmx-todoapp","commit_stats":null,"previous_names":["emarifer/go-frameworkless-htmx-todoapp"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emarifer%2Fgo-frameworkless-htmx-todoapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emarifer%2Fgo-frameworkless-htmx-todoapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emarifer%2Fgo-frameworkless-htmx-todoapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emarifer%2Fgo-frameworkless-htmx-todoapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emarifer","download_url":"https://codeload.github.com/emarifer/go-frameworkless-htmx-todoapp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249067779,"owners_count":21207396,"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":["authentication-middleware","centralized-logging","daisyui","error-handling","error-middleware","flash-messages","frameworkless","golang","hateoas","htmx","hypermedia","hyperscript","jwt-authentication","layered-architecture","onion-architecture","server-side-rendering","sweetalert2","tailwindcss","template-engine"],"created_at":"2024-11-16T12:17:51.825Z","updated_at":"2025-04-15T12:15:31.413Z","avatar_url":"https://github.com/emarifer.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Full stack Demo app made in frameworkless Go (Todo App 🧬), centralized HTTP error handling, CRUD to a SQLite database and HTMx-powered frontend\n\n\u003cimg src=\"assets/img/gopher_logo.webp\" width=\"35%\"\u003e\n\n\u003chr /\u003e\n\n![GitHub License](https://img.shields.io/github/license/emarifer/go-frameworkless-htmx-todoapp) ![Static Badge](https://img.shields.io/badge/Go-%3E=1.22-blue)\n\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003e[!NOTE]\n\u003e***This application is an clone of this [repository](https://github.com/emarifer/rust-axum-askama-htmx-todoapp) of mine (rust-axum-askama-htmx-todoapp), but made in `Golang`.***\n\n\u003chr /\u003e\n\n### 🤔 Why is the standard library all you need? (or almost)\u0026nbsp;\u003cimg src=\"assets/img/gopher-dance-long-3x.gif\" width=\"50\" align=\"center\"\u003e\n\nIn the world of Go, the Unix philosophy is followed which says:\n\n- Write programs that do one thing and do it well.\n- Write programs to work together.\n- Write programs to handle text streams because that is a universal interface.\n\nThis is why Go users tend to avoid using (perhaps abusing) frameworks and libraries. This way of thinking has one result: our project is more stable and maintainable. You can find these ideas more developed in this [post](https://threedots.tech/post/best-go-framework/). All of this is more true since version `v.1.22` of Go was released, in which notable improvements in the `net/http` package allow advanced routing techniques that until now we could only achieve comfortably through the use of other libraries.\n\n\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"assets/img/graph.webp\" width=\"37%\"\u003e\n\n###### It's practical to change parts of your project without killing it.\n\n\u003c/div\u003e\n\nIt's true that \"doing things yourself\" is going to involve a certain amount of boilerplate. But with Go until you build large applications the balance between boilerplate and stability/maintainability will remain at acceptable levels.\n\nI present here an application template for beginners (a todoapp, always the beginning for Web development 🤦) that includes the most frequent topics: routing, middlewares, centralized error handling, authentication with jsonwebtoken, use of the Go context to pass request-scoped data and some others, in addition to some small \"hacky trick\" 😀.\n\nI want to make it clear that none of the parts that make up this application together constitute a recipe to create a whole. My purpose in creating it has been that everything works as expected. They only constitute a list of small pieces/solutions that you can (and should) use separately when creating your projects.\n\nIn any case, solving these problems, common in backend/fullstack development, has a consequence: you learn Go (or any other programming language) better and faster.\n\nFor all these reasons, I have given up using libraries/frameworks. There are only 3 dependencies in this project:\n\n- one for hashing passwords\n- another for authentication with JWT\n- finally the driver for the Sqlite3 DB that is used for storage\n\nNone of them have indirect dependencies.\n\nThe architecture follows a typical \"onion model\" where each layer doesn't know about the layer above it, and each layer is responsible for a specific thing. Although the application is extremely simple, we use this pattern to illustrate its use in more complex applications.\n\nLayering an application in this way can simplify code structure, since the responsibility of each type is clear.\n\nTo ensure that each part of the application is initialized with its dependencies, each struct defines a constructor (the New function in this example).\n\n\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"assets/img/structure.svg\" width=\"90%\"\u003e\n\n\u003c/div\u003e\n\nThe use of [\u003c/\u003ehtmx](https://htmx.org/) allows behavior similar to that of a SPA, without page reloads when switching from one route to another or when making requests (via AJAX) to the backend. Reading Carson Gross's book [Hypermedia System](https://hypermedia.systems/) is very illuminating.\n\nOn the other hand, the \u003c/\u003ehtmx `response-targets` [extension](https://v1.htmx.org/extensions/response-targets/) allows you to specify different target elements that will be exchanged when different HTTP response codes are received. In our case it makes it easier to replace the entire response body with the corresponding error page.\n\nThe styling of the views is achieved through `Tailwind CSS` and `DaisyUI` that are obtained from their respective CDNs.\n\nLikewise, the `SweetAlert2` library is used, a substitute for JavaScript pop-up boxes. In the same way it is obtained from its respective CDN.\n\nFinally, minimal use of [_hyperscript](https://hyperscript.org/) is made to achieve the action of closing the alerts when they are displayed or giving interactivity to the show/hide password button in its corresponding input.\n\n---\n\n### Features 🚀\n\n- [x] **Use of \"native\" middlewares:** Middleware chaining has been solved with an elegant and reusable solution to avoid having to wrap one middleware inside another if your application requires many of them.\n- [x] **Centralized error management:** Middleware is also used to handle errors centrally. More specifically, since handlers are what return an error, the Adapter design pattern is used when implementing the ServeHTTP method (of the http.Handler interface), which handles errors.\n- [x] **Flash Messages:** They give the user information about the result of their actions (success/error). No third-party library is used to implement this feature.\n- [x] **Using Go's native templating engine:** Although the `a-h/templ` [library](https://github.com/a-h/templ) allows type checking of the data we pass to our templates, I believe that even medium-sized projects the security/coding speed ratio is more favorable with native Go templates... and with zero dependencies.\n- [x] **Authentication with JWT:** which frees the server from saving user data (in memory or in DB). Furthermore, the library used does not have indirect dependencies.\n- [x] **Structured Logging with slog:** I have \"wrapped\" the API of the `slog` package to customizing it and make it prettier. The logger prints both the output of the handlers or their result completed with an error, as well as the information related to the application's assets.\n- [x] **Using the JavaScript library for front-end `htmx`:** Obtained via their CDN.\n- [x] **Using interfaces in the `services` package:** The architecture follows a typical \"onion model\" where each layer doesn't know about the layer above it, and each layer is responsible for a specific thing, in this case, the `services` (package) layer, which allows for better separation of responsibilities and `dependency injection`.\n\n---\n\n### 🖼️ Screenshots:\n\n\u003cdiv align=\"center\"\u003e\n\n###### Terminal showing app logger:\n\n\u003cimg src=\"assets/img/screenshot-10.png\" width=\"60%\" align=\"top\"\u003e\n\n###### Todo List Page with success alert \u0026 Sign Up Page with error alert:\n\n\u003cimg src=\"assets/img/screenshot-1.png\" width=\"40%\" align=\"top\"\u003e\u0026nbsp;\u0026nbsp;\u003cimg src=\"assets/img/screenshot-2.png\" width=\"40%\" align=\"top\"\u003e\n\n\u003cbr\u003e\n\n###### Task update page \u0026 popup alert based on SweetAlert2:\n\n\u003cimg src=\"assets/img/screenshot-3.png\" width=\"40%\" align=\"top\"\u003e\u0026nbsp;\u0026nbsp;\u003cimg src=\"assets/img/screenshot-6.png\" width=\"40%\" align=\"top\"\u003e\n\n\u003cbr\u003e\n\n###### Centralized handling of 404 \u0026 500 errors:\n\n\u003cimg src=\"assets/img/screenshot-4.png\" width=\"40%\" align=\"top\"\u003e\u0026nbsp;\u0026nbsp;\u003cimg src=\"assets/img/screenshot-5.png\" width=\"40%\" align=\"top\"\u003e\n\n\u003cbr\u003e\n\n\u003c/div\u003e\n\n---\n\n### 👨‍🚀 Getting Started:\n\nBesides the obvious prerequisite of having Go on your machine, you must have [Air](https://github.com/air-verse/air) installed for hot reloading when editing code.\n\n\nStart the app in development mode:\n\n```\n$ air # Ctrl + C to stop the application\n```\n\nBuild for production:\n\n```\n$ go build -ldflags=\"-s -w\" -o ./bin/go-frameworkless-htmx ./cmd/go-frameworkless-htmx/main.go # ./bin/main to run the application / Ctrl + C to stop the application\n```\n---\n\n### Happy coding 😀!!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femarifer%2Fgo-frameworkless-htmx-todoapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femarifer%2Fgo-frameworkless-htmx-todoapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femarifer%2Fgo-frameworkless-htmx-todoapp/lists"}