{"id":19044596,"url":"https://github.com/brakmic/hda_with_cpp","last_synced_at":"2025-04-23T23:29:12.390Z","repository":{"id":83787187,"uuid":"583446888","full_name":"brakmic/HDA_with_Cpp","owner":"brakmic","description":"Hypermedia-driven application based on htmx and Drogon C++ web framework","archived":false,"fork":false,"pushed_at":"2023-05-17T03:51:31.000Z","size":9937,"stargazers_count":35,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-18T08:39:31.414Z","etag":null,"topics":["cpp","cpp-programming","html","htmx","hypermedia","javascript","responsive-web-design","rest-api","web-application"],"latest_commit_sha":null,"homepage":"https://hypermedia.software","language":"C++","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/brakmic.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,"zenodo":null}},"created_at":"2022-12-29T20:07:13.000Z","updated_at":"2025-03-02T17:59:25.000Z","dependencies_parsed_at":null,"dependency_job_id":"bb774af0-67b2-4899-a3ca-d30015167912","html_url":"https://github.com/brakmic/HDA_with_Cpp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brakmic%2FHDA_with_Cpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brakmic%2FHDA_with_Cpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brakmic%2FHDA_with_Cpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brakmic%2FHDA_with_Cpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brakmic","download_url":"https://codeload.github.com/brakmic/HDA_with_Cpp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250531420,"owners_count":21445981,"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":["cpp","cpp-programming","html","htmx","hypermedia","javascript","responsive-web-design","rest-api","web-application"],"created_at":"2024-11-08T22:46:49.045Z","updated_at":"2025-04-23T23:29:12.377Z","avatar_url":"https://github.com/brakmic.png","language":"C++","readme":"### Hypermedia-driven app built with **htmx** and **C++**\n\n![htmx_with_cpp](videos/htmx_with_cpp.gif)\n\n- [Introduction](#introduction)\n    - [Article](#article)\n  - [htmx](#htmx)\n  - [\\_hyperscript](#_hyperscript)\n  - [Why C++ for the backend?](#why-c-for-the-backend)\n- [Setup](#setup)\n  - [MacOS, Linux](#macos-linux)\n  - [Windows](#windows)\n    - [MSYS](#msys)\n    - [Build system](#build-system)\n    - [Drogon](#drogon)\n    - [Static libraries](#static-libraries)\n  - [Meson](#meson)\n- [Application architecture](#application-architecture)\n- [Project structure](#project-structure)\n- [Tests](#tests)\n  - [macOS / Linux](#macos--linux)\n  - [Windows](#windows-1)\n- [Hypermedia-driven app](#hypermedia-driven-app)\n  - [Program arguments](#program-arguments)\n  - [Drogon configuration file](#drogon-configuration-file)\n  - [Web Server configuration file](#web-server-configuration-file)\n- [CHANGELOG](#changelog)\n- [LICENSE](#license)\n\n-----\n## Introduction\n\nThis repository contains an [HDA](https://htmx.org/essays/hypermedia-driven-applications/) based on [htmx](https://htmx.org/) (frontend) and [Drogon C++ framework](https://drogon.org/) (backend). \n\nThe aim was to create a responsive \"web app\" without using any of the usual JavaScript frameworks.\n\nThe idea for this project came while reading the excellent book [Hypermedia Systems](https://hypermedia.systems/). In it, the authors talk about alternative ways for writing `modern` web applications. Unlike most of the other books on web development, the authors don't rely on any JavaScript framework, but instead go back to the roots of the hypermedia architecture that is `the web` itself.\n\n#### Article\n\nI've also written [an article](https://blog.brakmic.com/writing-hdas-with-htmx-and-c/) about this project and my general motivation to use htmx and C++.\n\n### htmx\n\nInstead of using JavaScript *to overcome* HTML, a strategy that basically reproduces thick-clients of the 90es, the authors use `htmx` **to augment** it. They make it capable of doing *more* without falling back to clever JavaScript tricks. Of course, JS isn't forbidden and `htmx` itself relies on it for its own development, but JS is not visible as there is no actual need for it.\n\nWe don't need to use JS to replace seemingly \"insufficient\" hypermedia controls, because **htmx** is here to extend them. It makes them capable of doing \n*more* as originally defined. An anchor tag (`\u003ca\u003e`), for example, can be \"upgraded\" so that it can execute POST, PUT, PATCH, or even DELETE requests. A `\u003cform\u003e` tag doesn't have to be the only hypermedia control for sending data via POST requests. How about writing your own controls that can do exactly the same? Or maybe `\u003cform\u003e`s that can PATCH existing entries on the server? What usually demands explicit JS code can now be done *declaratively* with *upgraded* hypermedia controls.\n\nHere's an example from this project. Two buttons (*Cancel* \u0026 *Save*) which can be found in almost every sufficiently complex web app.\n\n```html\n\u003cbutton hx-get=\"/contacts\"\n        hx-target=\"#main\"\n        hx-swap=\"innerHTML\"\u003e\n        Cancel\n\u003c/button\u003e\n\u003cbutton hx-post=\"/contacts/{%contact.ID%}/edit\"\n        hx-include=\"input\"\n        hx-target=\"#main\"\n        hx-swap=\"innerHTML\"\u003e\n        Save\n\u003c/button\u003e\n```\n\nBelieve it or not, but these two utilize the following functionalities:\n\n* Executing AJAX requests.\n* Using HTTP verbs that are usually not available for `\u003cbutton\u003e` controls.\n* Passing of additional element values in AJAX requests.\n* Transclusion (that is, *where* and *how* to insert the server response data)\n\nAnd not a single line of JavaScript was needed to make it work. This is how powerful hypermedia architecture actually is.\n\n### _hyperscript\n\nWe also use [_hyperscript](https://hyperscript.org/), a small library for event handling and DOM manipulation. With it, we can listen to and dispatch events, manipulate DOM objects, all without leaving HTML.\n\nHere's an example from this project:\n\n```html\n\u003cbutton id=\"edit-c\" class=\"btn btn-primary\"\n      hx-get=\"/contacts/{%c.ID%}/edit\"\n      hx-target=\"#main\"\n      hx-swap=\"innerHTML\"\u003eEdit\u003c/button\u003e\n\u003cbutton class=\"btn btn-danger\"\n      hx-delete=\"/contacts/{%c.ID%}/delete\"\n      hx-confirm=\"Are you sure you wish to delete this contact?\"\n      hx-target=\"this\"\n      hx-swap=\"none\"\n      _=\"on click remove #edit-c\n                  then remove me\"\n      \u003eDelete\u003c/button\u003e\n\u003cbutton class=\"btn btn-info\"\n      hx-get=\"/contacts\"\n      hx-target=\"#main\"\n      hx-swap=\"innerHTML\"\u003eBack\u003c/button\u003e\n```\n\nIn the second `\u003cbutton\u003e` control we have a few bits of _hyperscript that does the following:\n\n* reacts to click events\n* then removes the control with *id=edit-c*\n* then removes the button that reacted to click event (it removes itself)\n\nThe final result is the removal of the buttons `Edit` and `Delete`. Only the button `Back` remains.\n\n![using_hyperscript](videos/using_hyperscript.gif)\n\n-----\n\nInstead of sending JSONs back and forth (*and each time parsing them according to some internal logic*), we can use HTML as originally designed: as a vehicle for meaningful hypermedia applications. The HTTP protocol exists because of HTML, but these days we mostly transfer JSON over it. This actually makes little sense, because JSON can't transport application semantics, which effectively cripples the original meaning of the *client-server archiecture* of the web. No wonder we need massive JS frameworks on our frontends, because our servers are mostly just data providers with JSON APIs. And JSON APIs aren't \"RESTful\".\n\n-----\n\n### Why C++ for the backend?\n\nThe book's example backend source code is written in Python and it can be used instead of C++. In fact, I have tried to mimic the original Python APIs, so that there should be no big gaps in understanding them both. I was writing the C++ code while reading the respective chapters.\n\nBut as `htmx` is very language agnostic, there is no problem of using any language whatsoever, so I used C++. This is also good from the learning perspective as it forces me to double check everything.\n\nI think that we should remove bloat not only from our frontends [*put any massive JS framework in here*], but also from our backends [*put any massive backend framework in here*]. Massive software consumes massive amounts of time and energy. **Human** time and energy as well as **CPU** cycles and electricity.\n\n## Setup\n\n### MacOS, Linux\n  \nA few C++ libraries are needed for the compilation to succeed. This project uses [vcpkg](https://vcpkg.io/en/index.html) as its package manager, but you are free to choose any other instead.\n\nTo install a package, simply invoke `vcpkg install PACKAGE_NAME`.\n\nThe following packages are needed:\n\n    drogon\n    drogon[ctl]\n    fmt\n    argparse\n    brotli\n    zlib\n    openssl\n    sqlite3\n    soci[core]\n    soci[sqlite3]\n\nThe search for them is easy: `vcpkg search PACKAGE_NAME`\n\n#### Install extra dependencies when building on Linux\n`sudo apt install uuid-dev libcriterion-dev`\n\n### Windows\n\n#### MSYS\n\nWindows users will have to setup [MSYS](https://www.msys2.org/) environment first. After the installation, select the `MSYS2 MINGW64` entry in the Windows Start Menu. **Do not use the `MSYS UCRT4` or any other entry!**\n\n#### Build system\n\nIn the newly opened bash window, enter this command to install the required packages:\n\n`pacman -S git mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake make mingw-w64-x86_64-c-ares mingw-w64-x86_64-jsoncpp mingw-w64-x86_64-openssl`\n\nCheck if the compiler is available with `which g++`. You should see a message like this:\n\n```shell\n$ which g++\n/mingw64/bin/g++\n```\n\nYou will also need an editor to update the environment paths, so install your preferred one, e.g. `pacman -Sy nano` or `pacman -Sy vim`\n\nOpen your `.bash_profile` with `nano .$HOME/.bash_profile` and add these three lines to the end of the file:\n\n\n```bash\nPATH=/mingw64/bin:$PATH\nexport VCPKG_DEFAULT_TRIPLET=x64-mingw-static\nexport VCPKG_DEFAULT_HOST_TRIPLET=x64-mingw-static\n```\n\nSave \u0026 close the file. Reload it with: `source $HOME/.bash_profile` or `. ~/.bash_profile`\n\nThe two triplet entries will be needed later to instruct `vcpkg` to use MinGW instead of the default Visual C++ compiler. And as we also want to compile static libraries only, we announce it by using the `static` suffix.\n\n#### Drogon\n\nUnlike other packages, Drogon will not be installed with `vcpkg`. The currently available vcpkg package thows compilation errors, which is the reason why we have to compile it manually.\n\nClone the Drogon sources and prepare the build environment. The `/c/bin/drogon` path from the example below should be adapted to your local settings. The root of this path (`/c/bin`) must map to an already existing path in the Windows system, e.g. `C:/bin` or any other path of your choosing.\n\n```bash\ngit clone https://github.com/drogonframework/drogon --recursive\nmkdir drogon/build\ncd drogon/build\ncmake .. -G \"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX:PATH=/c/bin/drogon\n```\n\nNow compile Drogon with `make -j` and wait until it completes. \n\nFinally, install Drogon with `make install`.\n\nYou should now see a list of folders in `C:/bin/drogon`.\n\n![drogon_dir](images/drogon_dir.png)\n\n#### Static libraries\n\nThe second step is the installation of a few libraries that will be linked statically. We will use `vcpkg` to compile them all.\n\nFrom the same bash window, issue the following commands to setup `vcpkg`. \n\n```bash\ncd $HOME\ngit clone https://github.com/microsoft/vcpkg.git\ncd vcpkg\n./bootstrap-vcpkg.bat\n```\n\n*Notice:* \n    \n    If you pefer to install vcpkg files under different root path, change the first command \"cd $HOME\" from the script above. \n\n    For example: cd /c/Users/WINDOWS_USER_NAME\n\n    In MSYS Bash, the Windows file system is located under /c.\n    And your MSYS $HOME folder is located under \"home\" in your Windows MSYS root folder.\n\nFrom the `vcpkg` folder, issue the following commands to install required libraries:\n\n```bash\n./vcpkg.exe install argparse\n./vcpkg.exe install fmt\n./vcpkg.exe install brotli\n./vcpkg.exe install zlib\n./vcpkg.exe install openssl\n./vcpkg.exe install sqlite3\n./vcpkg.exe install soci\n./vcpkg.exe install soci[sqlite3]\n```\n\nNow you can compile this project via PoweShell with `./buildall.ps1`.\n\nBut, don't forget to change `vcpkg_root` in `meson.build` first. This path should point at the previously cloned `vcpkg` repository.\n\n![windows_build](videos/windows_build.gif)\n\n### Meson\n\nMy build system of choice is [Meson](https://mesonbuild.com/), because `Makefiles` are hard to maintain and I simply don't want to learn how to use `CMake`. Life is too short for user-hostile software.\n\nThere are two scripts, `buildall.sh` (macOS/Linux) and `buildall.ps1` (Windows). With these two the following steps will be executed:\n\n* Copy web files (index.html, styles etc.) to `builddir` (*only on Windows, in macOS/Linux this will be done by Meson*)\n* Initialize and run Meson:\n   * use `drogon_ctl` to convert CSPs into C++ source files and put them into `src/views`\n   * compile sources from `src`\n   * put the output binary into `builddir`\n\nA C++20 compiler is needed. I'm using GNU C++ v12.1.0.\n\nBefore trying to build the project, please, adapt these two variables in the `meson.build` file:\n\n* [triplet](meson.build#L26)\n* [vcpkg_root](meson.build#L34)\n\nThe `triplet` carries the information about the host machine, e.g. `x64-osx`.\n\nThe `vcpkg_root` is the root folder containing packages installed by `vcpkg`.\n\n`drogon_ctl` will be used by `Meson` to convert CSP templates into C++ files.\n\n![compile_project](videos/compile_project.gif)\n\n## Application architecture\n\nThe frontend uses the **htmx** library and some `Bootstrap` resources for styling. There is no hand-written JavaScript running as **htmx** already provides the `responsive` stuff we expect any `modern` web app to offer.\n\nThe backend is based on the very fast C++ web framework called `Drogon`.\n\nThe database in use is SQLite3 but it can be replaced easily with any other SQL database. Simply adjust the `src/database/db_mgr.cpp` class. The library for accessing SQLite3 is [SOCI](https://soci.sourceforge.net/doc/release/4.0/) and it supports many other database backends. The root of this project contains a SQLite3 file, `demo.db`, that the app uses by default. There is also a CSV file available, `contacts.csv`, that contains a few entries that can be used to populate a new table.\n\n## Project structure\n\n![project_structure](images/project_structure.png)\n\n* `controllers` contains classes that Drogon uses to map client calls to functions in the backend.\n* `database` contains a small wrapper class for accessing the SQLite3 instance.\n* `dtos` contains `Data Transfer Objects` that are used for data tansfers between frontend and backend.\n* `templates` contains [CSPs](https://github.com/drogonframework/drogon-docs/blob/master/ENG-06-View.md) (C++ Server Pages), which are templates that `drogon_ctl` uses to generate C++ sources. These sources will be used to create HTML outputs.\n* `views` contains Drogon-generated C++ classes. These files **should not be edited manually**. They will be replaced on every build. To change their behavior or contents, use CSPs from `templates` folder instead.\n\n## Tests\n\nTests are done with the [Criterion](https://github.com/Snaipe/Criterion) library. \n\n### macOS / Linux\n\nCriterion can be installed via `brew install criterion`. Otherwise, you can manually build it as described [here](https://criterion.readthedocs.io/en/latest/setup.html#installation).\n\n### Windows\n\nTo build Criterion with `Meson`, clone its repo first:\n\n```bash\ngit clone --recursive https://github.com/Snaipe/Criterion.git\n```\n\nThen issue the following commands:\n\n```powershell\ncd Criterion\nmeson -Dprefix=c:/bin/criterion build\nninja -C build install\n```\n\nThe installation directory prefix can be changed. After the installation is completed, set the path to Criterion's DLL file. This DLL will be used by test executables that have Criterion linked.\n\n![criterion_dll_path](images/criterion_dll_path.png)\n\nThe test sources of this project are located in `test` and are being built automatically by `Meson`. To execute tests, you can use these two options:\n\n```powershell\nPS \u003e meson test -C .\\builddir\\\nninja: no work to do.\nninja: Entering directory `.\\builddir'\nninja: no work to do.\n1/1 basic        OK              0.09s\n\nOk:                 1\nExpected Fail:      0\nFail:               0\nUnexpected Pass:    0\nSkipped:            0\nTimeout:            0\n\nFull log written to .\\builddir\\meson-logs\\testlog.txt\n```\n\nOr by directly calling the test executable itself:\n\n```powershell\nPS \u003e .\\builddir\\test_demo_web_server.exe\n[====] Synthesis: Tested: 1 | Passing: 1 | Failing: 0 | Crashing: 0\n```\n\n## Hypermedia-driven app\n\nThe web application starts by loading the `index.html` which contains a `div` tag with *id=\"main\"*. Throughout the app, this tag will be used by other controls to dynamically replace its contents without any page refreshes. However, unlike other typical `modern` web apps, we use no JS frameworks like React or Angular to make the app responsive. Instead, we only use `htmx` as our scripting library.\n\nThere are also three `bootstrap` resources involved, but this is just make the app look better. Bootstrap is not a requirement and can be replaced by any other library or own stylesheets. The same applies to `jQuery` that is included as a bootstrap dependency. Any of those libraries can be safely removed as they don't affect `htmx` or `_hyperscript`.\n\nThe web app communicates with the server in a standard request-response fashion. But unlike so many other web apps out there, no JSON is being used. Instead, the server is only sending pieces of HTML code that the client uses to update the current state of the app.\n\n### Program arguments\n\nThe server program accepts two parameters for setting the IP and Port.\n\n```bash                    \nUsage: demo_web_server [options] \n\nOptional arguments:\n-h --help               shows help message and exits [default: false]\n-v --version            prints version information and exits [default: false]\n-i --ip-address         Server IP Address [default: \"127.0.0.1\"]\n-p --port               Port [default: 3000]\n```\n\n![use_arguments](videos/use_arguments.gif)\n\n### Drogon configuration file\n\nYou can also use the included Drogon's `config.json` to control the behavior of the server. As Drogon offers [lots of options](https://github.com/drogonframework/drogon/blob/master/config.example.json), you should first [make yourself familiar with it](https://github.com/drogonframework/drogon-docs/blob/master/ENG-10-Configuration-File.md). The configuration file in this project contains only a few settings.\n\n### Web Server configuration file\n\nThere also exist a separate JSON-based configuration file,`server_config.json`, that will be used by the web server. Currently, it only defines the location of the SQLite3 file, but it will be expanded in the future.\n\n```json\n{\n  \"database\": {\n    \"type\": \"sqlite3\",\n    \"file\": \"demo.db\"\n  }\n}\n```\n\nThis file should not be confused with Drogon's own JSON which is named `config.json`. \n\n## CHANGELOG\n* 30/12/2022:    \n    -  added _hyperscript scripts in index.html\n    -  added jQuery in index.html (bootstrap needs it)\n    -  changed button behavior when deleting contacts\n    -  added example with _hyperscript\n    -  added gif demo showcasing _hyperscript\n* 31/12/2022:\n    -  Windows compilation support\n* 01/01/2023:\n    -  added TOC to README\n    -  added FontAwesome\n    -  added style.css\n    -  updated index.html\n    -  updated buildall.ps1\n    -  updated meson.build\n    -  included Drogon's config.json\n    -  use Drogon's AOP to display active Listeners\n* 02/01/2023:\n    - added Criterion test library\n    - added server configuration facility\n    - added server configuration JSON\n    - updated README regarding testing\n* 17/05/2023\n    - fixed problem with building on Linux Ubuntu (patches by [@csharpee](https://github.com/csharpee))\n\n## LICENSE\n\n[MIT](LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrakmic%2Fhda_with_cpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrakmic%2Fhda_with_cpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrakmic%2Fhda_with_cpp/lists"}