{"id":26657654,"url":"https://github.com/darren277/restettastone","last_synced_at":"2026-04-09T15:43:09.666Z","repository":{"id":257892711,"uuid":"864532886","full_name":"darren277/RESTettaStone","owner":"darren277","description":"Showcase of REST API functionality, Dockerized, for 30+ programming languages and 40+ frameworks.","archived":false,"fork":false,"pushed_at":"2025-03-27T14:21:16.000Z","size":2583,"stargazers_count":0,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-31T14:43:20.579Z","etag":null,"topics":["bash","cpp","csharp","dlang","docker","fsharp","go","haskell","java","javascript","lisp","perl","php","prolog","python","ruby","swift","zig"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darren277.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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-09-28T13:26:12.000Z","updated_at":"2025-03-24T15:55:54.000Z","dependencies_parsed_at":"2024-11-07T12:26:48.952Z","dependency_job_id":"caed9411-8b95-4be8-a6c7-9c8f00882009","html_url":"https://github.com/darren277/RESTettaStone","commit_stats":null,"previous_names":["darren277/restettastone"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/darren277/RESTettaStone","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darren277%2FRESTettaStone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darren277%2FRESTettaStone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darren277%2FRESTettaStone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darren277%2FRESTettaStone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darren277","download_url":"https://codeload.github.com/darren277/RESTettaStone/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darren277%2FRESTettaStone/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279177167,"owners_count":26119899,"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","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["bash","cpp","csharp","dlang","docker","fsharp","go","haskell","java","javascript","lisp","perl","php","prolog","python","ruby","swift","zig"],"created_at":"2025-03-25T09:15:50.427Z","updated_at":"2025-10-16T10:16:24.252Z","avatar_url":"https://github.com/darren277.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# TOC\n\n- [About](#about)\n  * [Parts](#parts)\n    + [Database](#database)\n  * [History](#history)\n  * [Current Status](#current-status)\n    + [Backend](#backend)\n    + [Frontend](#frontend)\n    + [Other](#other)\n  * [Standing on the Shoulders of Giants](#standing-on-the-shoulders-of-giants)\n  * [Future](#future)\n  * [See Also](#see-also)\n    + [Theoretical](#theoretical)\n    + [Practical](#practical)\n- [How to Use](#how-to-use)\n  * [Configuring Environment Variables](#configuring-environment-variables)\n    + [`.env` File](#env-file)\n    + [Setting Environment Variables in Redis and Nginx](#setting-environment-variables-in-redis-and-nginx)\n  * [Using the Makefile](#using-the-makefile)\n    + [Overview of Commands](#overview-of-commands)\n      - [Docker](#docker)\n  * [Using Docker Compose](#using-docker-compose)\n  * [Testing](#testing)\n    + [Simple Endpoint Testing with `Make`](#simple-endpoint-testing-with-make)\n    + [Using Python Script for More Advanced Testing](#using-python-script-for-more-advanced-testing)\n  * [Performance Testing with Locust](#performance-testing-with-locust)\n  * [Monitoring](#monitoring)\n    + [Prometheus and Grafana](#prometheus-and-grafana)\n  * [Cloud Deployment](#cloud-deployment)\n    + [AWS](#aws)\n      - [EC2](#ec2)\n        * [Some Docker Notes](#some-docker-notes)\n          + [Docker Permissions](#docker-permissions)\n          + [Testing Endpoint with Curl](#testing-endpoint-with-curl)\n        * [Some Notes About Make and SSH Commands](#some-notes-about-make-and-ssh-commands)\n        * [SSH Keys](#ssh-keys)\n          + [Key File Permissions](#key-file-permissions)\n          + [Key Format](#key-format)\n  * [RabbitMQ](#rabbitmq)\n- [Lessons](#lessons)\n  * [DevOps Gotchas](#devops-gotchas)\n    + [Nginx Gotchas](#nginx-gotchas)\n      - [Environment Variables](#environment-variables)\n    + [Docker Gotchas](#docker-gotchas)\n      - [ARGS and Multi Stage Builds](#args-and-multi-stage-builds)\n      - [Exec Form vs Shell Form for ENTRYPOINT:](#exec-form-vs-shell-form-for-entrypoint-)\n    + [Docker Compose Gotchas](#docker-compose-gotchas)\n      - [Dynamic Build Args](#dynamic-build-args)\n    + [Make Gotchas](#make-gotchas)\n      - [Tabs vs Spaces](#tabs-vs-spaces)\n  * [Language and Framework Gotchas](#language-and-framework-gotchas)\n    + [Symfony (PHP)](#symfony--php-)\n      - [Headers vs Body and the Whitespace Issue](#headers-vs-body-and-the-whitespace-issue)\n      - [CLI Race Condition](#CLI-Race-Condition)\n    + [Rocket (Rust)](#rocket--rust-)\n      - [Pear Codegen Dependency](#pear-codegen-dependency)\n    + [Django (Python)](#django--python-)\n      - [Project Structure](#project-structure)\n      - [URL Routing](#url-routing)\n      - [Django REST Framework](#django-rest-framework)\n    + [Zig](#zig)\n      - [Postgres Connection Pool Inconsistency Problem](#postgres-connection-pool-inconsistency-problem)\n    + [Fat Free (PHP)](#fat-free--php-)\n      - [Dependency Source](#dependency-source)\n  * [Database Gotchas](#database-gotchas)\n    + [Postgres](#postgres)\n      - [pg_stat_activity Locks](#pg_stat_activity-locks)\n  * [Other General Lessons](#other-general-lessons)\n    + [Branch Protection](#branch-protection)\n  * [Comparisons](#comparisons)\n    + [Projects by Language](#projects-by-language)\n      - [Classic](#classic)\n      - [Performant](#performant)\n      - [Procedural](#procedural)\n      - [Object Oriented](#object-oriented)\n      - [Functional](#functional)\n      - [Multi-Paradigm](#multi-paradigm)\n      - [Versatile or Other](#versatile-or-other)\n      - [JavaScript](#javascript)\n    + [Metrics](#metrics)\n      - [Qualitative](#qualitative)\n        - [Simplicity](#simplicity)\n      - [Quantitative](#quantitative)\n        - [Performance](#performance)\n      - [Both Qualitative and Quantitative](#both-qualitative-and-quantitative)\n        - [Security](#security)\n        - [Safety](#safety)\n- [Optimization](#optimization)\n  * [Docker Image Size](#docker-image-size)\n    + [Table Sorted Alphabetically](#table-sorted-alphabetically)\n    + [Table Sorted by Image Size](#table-sorted-by-image-size)\n\n# About\n\nThis is a project I started back in Fall of 2022. The purpose is to have a repository showcasing basic REST API functionality, Dockerized, for a multitude of programming languages and frameworks.\n\n## Parts\n\nThis repository consists of a `backend` and `frontend` folder, each in turn consisting of subdirectories for each framework or server implementation.\n\nI also include a `Dockerfile` inside of each subdirectory so that each mini app can be run in its own container.\n\nFurthermore, I use Nginx to route requests to each backend.\n\nThere is also a `Makefile` with the commands to build and run each container, as well as some general purpose commands for creating the network, handling logs and simple container interaction tests, etc.\n\n### Database\n\nThis project uses a single Postgres database for all backends to interact with.\n\n## History\n\nI started this back in 2022 and made a lot of headway with it in the beginning, but ended up putting it aside in November of 2022 as I just started a new job and simply did not have the time to continue adding to it.\n\nI found myself dusting it off a couple of times, briefly, during 2024.\n\nI've decided to set out to complete as much of what's left as I can now, hoping to maybe finish it before the year is out.\n\n## Current Status\n\nThis table represents the status of the project as of November 2022.\n\nI'm leaving the Python options (Flask and Django) until last as they will be by far the easiest to implement for me.\n\nI break down the barriers I've faced into a few fundamental categories:\n* **Language**: A lack of experience with the language and some of its specific nuances. This barrier is especially prevalent with languages that use non-object-oriented paradigms (for example, Lisp, Haskell, Prolog, etc).\n* **Libraries**: Since I'm using Postgres for the universal database that all the backends are accessing, I need an existing library or implementation for accessing a Postgres database. I think the only place where this was a total blocker was Bun.js. I started looking into the feasibility of writing my own super simple implementation, but abandoned that due to a lack of available time. It turns out, after a couple years have passed, that there is now Postgres support for Bun.js.\n* **Networking**: This is a case where I have difficulties accessing the endpoint for some reason. I recall a particular scenario where `0.0.0.0` was not doing what it was supposed to be doing, and I just hit a wall with it and gave up at the time. Looking at the table, it looks like that was one of the Rust frameworks. I plan to revisit that and troubleshoot it further.\n* **Build**: This is where the Dockerfile fails to execute every step and the application simply does not run. It looks like Zig was the only such scenario. This is another one I will try revisiting after all this time.\n\nNote that I use `✅` to indicate that the task is completed, tested, and merged into `main`. I use `❌` to indicate that it is yet to be completed and pushed into `main`. ~~I use `'` to indicate that the task was completed at some point (likely two years ago), and is going through a testing stage before a PR is made.~~\n\nI also have two asterisks (`*`) but I can't remember what those were for. I'm leaving them in for now.\n\n### Backend\n\n|                                                                                                          | Dockerized | REST Responses | Postgres Interaction | Full CRUD | Current Barrier      |\n|----------------------------------------------------------------------------------------------------------|------------|----------------|----------------------|-----------|----------------------|\n| **BASH**                                                                                                 |            |                |                      |           |                      |\n| [Bash](https://github.com/darren277/RESTettaStone/tree/master/backend/bash/bashapp)                      | ✅          | ✅              | ✅                    | ✅         |                      |\n| **C**                                                                                                    |            |                |                      |           |                      |\n| [C](https://github.com/darren277/series-of-tubes)**                                    | ✅          | ✅              | ❌                    | ❌         |                      |\n| **C++**                                                                                                  |            |                |                      |           |                      |\n| [Crow (C++)](https://github.com/darren277/RESTettaStone/tree/master/backend/cpp/crowapp)                 | ✅          | ✅              | ✅                    | ✅         |                      |\n| **C#**                                                                                                   |            |                |                      |           |                      |\n| [Asp.Net (C#)](https://github.com/darren277/RESTettaStone/tree/master/backend/csharp/aspnetapp)          | ✅          | ✅              | ✅                    | ✅         |                      |\n| **CLOJURE**                                                                                              |            |                |                      |           |                      |\n| [Kit (Clojure)](https://github.com/darren277/RESTettaStone/tree/master/backend/clojure/kitapp)           | ✅          | ✅              | ✅                    | ✅         |                      |\n| **COBOL**                                                                                                |            |                |                      |           |                      |\n| [Cobol](https://github.com/darren277/bank)**                                                             | ✅        | ✅              | ✅                    | ❌          | Language             |\n| **D**                                                                                                    |            |                |                      |           |                      |\n| [Vibe (D)](https://github.com/darren277/RESTettaStone/tree/master/backend/d/vibeapp)                 | ✅          | ✅              | ✅                    | ✅         | ~~Networking~~       |\n| **DART**                                                                                                 |            |                |                      |           |                      |\n| [Shelf (Dart)](https://github.com/darren277/RESTettaStone/tree/master/backend/dart/shelfapp)             | ✅          | ✅              | ✅                    | ✅         |                      |\n| **ELIXIR**                                                                                               |            |                |                      |           |                      |\n| [Phoenix (Elixir)](https://github.com/darren277/RESTettaStone/tree/master/backend/elixir/phoenixapp)    | ✅          | ✅              | ✅                    | ✅         |                      |\n| **FORTRAN**                                                                                              |            |                |                      |           |                      |\n| [Fortran](https://github.com/darren277/risk)**                                                           | ❌          | ❌              | ✅                    | ❌          | Language             |\n| **F#**                                                                                                   |            |                |                      |           |                      |\n| [F#](https://github.com/darren277/RESTettaStone/tree/master/backend/fsharp/fsharpapp)                    | ✅          | ✅              | ✅                    | ✅         | ~~Language~~         |\n| **GO**                                                                                                   |            |                |                      |           |                      |\n| [Go](https://github.com/darren277/RESTettaStone/tree/master/backend/go/goapp)                            | ✅          | ✅              | ✅                    | ✅         |                      |\n| **HASKELL**                                                                                              |            |                |                      |           |                      |\n| [Spock](https://github.com/darren277/RESTettaStone/tree/master/backend/haskell/spockapp)                 | ✅          | ✅              | ✅                    | ✅         | ~~Language~~         |\n| **JAVA**                                                                                                 |            |                |                      |           |                      |\n| [Tomcat (Java)](https://github.com/darren277/RESTettaStone/tree/master/backend/java/tomcatapp)           | ✅          | ✅              | ✅                    | ✅         |                      |\n| [SpringBoot (Java)](https://github.com/darren277/RESTettaStone/tree/master/backend/java/springbootapp)   | ✅          | ✅              | ✅                    | ✅         |                      |\n| **JAVASCRIPT**                                                                                           |            |                |                      |           |                      |\n| [Bun (JS)](https://github.com/darren277/RESTettaStone/tree/master/backend/javascript/bunapp)             | ✅          | ✅              | ✅                    | ✅         | ~~Libraries~~        |\n| [Firebase (JS)](https://github.com/darren277/RESTettaStone/tree/master/backend/javascript/firebaseapp)   | ✅          | ✅              | N/A                  | ✅         |                      |\n| [Node (JS)](https://github.com/darren277/RESTettaStone/tree/master/backend/javascript/nodeapp)           | ✅          | ✅              | ✅                    | ✅         |                      |\n| **JULIA**                                                                                                |            |                |                      |           |                      |\n| [Genie (Julia)](https://github.com/darren277/RESTettaStone/tree/master/backend/julia/genieapp)           | ✅          | ✅              | ✅                    | ✅         |                      |\n| **KOTLIN**                                                                                               |            |                |                      |           |                      |\n| [Http4k (Kotlin)](https://github.com/darren277/RESTettaStone/tree/master/backend/kotlin/http4kapp)       | ✅          | ✅              | ✅                    | ✅         |                      |\n| **LISP**                                                                                                 |            |                |                      |           |                      |\n| [Hunchentoot (Lisp)](https://github.com/darren277/RESTettaStone/tree/master/backend/lisp/hunchentootapp) | ✅          | ✅              | ✅                    | ✅         | ~~Language~~         |\n| **LUA**                                                                                                  |            |                |                      |           |                      |\n| [Lua / OpenResty](https://github.com/darren277/RESTettaStone/tree/master/backend/lua/luaapp)             | ✅          | ✅              | ✅                    | ✅         |                      |\n| **PASCAL**                                                                                               |            |                |                      |           |                      |\n| [Pascal](https://github.com/darren277/RESTettaStone/tree/master/backend/pascal/pascalapp)                | ✅          | ✅              | ✅                    | ✅          |                      |\n| **PERL**                                                                                                 |            |                |                      |           |                      |\n| [Perl](https://github.com/darren277/RESTettaStone/tree/master/backend/perl/perlapp)                      | ✅          | ✅              | ✅                    | ✅         |                      |\n| **PHP**                                                                                                  |            |                |                      |           |                      |\n| [Fat Free (PHP)](https://github.com/darren277/RESTettaStone/tree/master/backend/php/fatfreeapp)          | ✅          | ✅              | ✅                    | ✅         |                      |\n| [Laravel (PHP)](https://github.com/darren277/RESTettaStone/tree/master/backend/php/laravelapp)           | ✅          | ✅              | ✅                    | ✅         |                      |\n| [PHP](https://github.com/darren277/RESTettaStone/tree/master/backend/php/phpapp)                         | ✅          | ✅              | ✅                    | ✅          |                      |\n| [Symfony (PHP)](https://github.com/darren277/RESTettaStone/tree/master/backend/php/symfonyapp)           | ✅          | ✅              | ✅                    | ✅         |                      |\n| **PROLOG**                                                                                               |            |                |                      |           |                      |\n| [Prolog](https://github.com/darren277/RESTettaStone/tree/master/backend/prolog/prologapp)                | ✅          | ✅              | ✅                    | ✅         | ~~Language~~         |\n| **PYTHON**                                                                                               |            |                |                      |           |                      |\n| [Django (Python)](https://github.com/darren277/RESTettaStone/tree/master/backend/python/djangoapp)       | ✅          | ✅              | ✅                    | ✅          |                      |\n| [Flask (Python)](https://github.com/darren277/RESTettaStone/tree/master/backend/python/flaskapp)         | ✅          | ✅              | ✅                    | ✅         |                      |\n| **R**                                                                                                    |            |                |                      |           |                      |\n| [Plumber (R)](https://github.com/darren277/RESTettaStone/tree/master/backend/r/plumberapp)               | ✅          | ✅              | ✅                    | ✅         |                      |\n| **RUBY**                                                                                                 |            |                |                      |           |                      |\n| [Rails (Ruby)](https://github.com/darren277/RESTettaStone/tree/master/backend/ruby/railsapp)             | ✅          | ✅              | ✅                    | ✅         |                      |\n| **RUST**                                                                                                 |            |                |                      |           |                      |\n| [Actix (Rust)](https://github.com/darren277/RESTettaStone/tree/master/backend/rust/actixapp)             | ✅          | ✅              | ✅                    | ✅         | ~~Language~~         |\n| [Rocket (Rust)](https://github.com/darren277/RESTettaStone/tree/master/backend/rust/rocketapp)           | ✅          | ✅              | ✅                    | ✅          | ~~Networking~~ ~~Build~~ |\n| **SCALA**                                                                                                |            |                |                      |           |                      |\n| [Play (Scala)](https://github.com/darren277/RESTettaStone/tree/master/backend/scala/playapp)             | ✅          | ✅              | ✅                    | ✅         |                      |\n| **SWIFT**                                                                                                |            |                |                      |           |                      |\n| [Swift](https://github.com/darren277/RESTettaStone/tree/master/backend/swift/swiftapp)                   | ✅          | ✅              | ✅                    | ✅         |                      |\n| **ZIG**                                                                                                  |            |                |                      |           |                      |\n| [Zig](https://github.com/darren277/RESTettaStone/tree/master/backend/zig/zigapp)                         | ✅          | ✅              | ✅                    | ✅*        | ~~Build~~            |\n\n(*) Zig app has full CRUD functionality, but there is an issue with connection pooling that causes it to behave inconsistently. [See notes below](#zig).\n\n(**) The COBOL and Fortran apps are a work in progress that are in separate repos while I build them out. The C web application is also a work in progress and is in its own repo.\n\n### Frontend\n\n|                                                                                              | Dockerized | Backend Integration | Full CRUD |\n|----------------------------------------------------------------------------------------------|------------|---------------------|-----------|\n| [Angular](https://github.com/darren277/RESTettaStone/tree/master/frontend/angularapp)        | ✅          | ✅                   | ✅         |\n| [Blazor (C#)](https://github.com/darren277/RESTettaStone/tree/master/frontend/blazorapp)     | ✅          | ✅                   | ✅         |\n| [Gatsby](https://github.com/darren277/RESTettaStone/tree/master/frontend/gatsbyapp)          | ✅          | ✅                   | ✅         |\n| [Next](https://github.com/darren277/RESTettaStone/tree/master/frontend/nextapp)              | ✅          | ✅                   | ✅         |\n| [React](https://github.com/darren277/RESTettaStone/tree/master/frontend/reactapp)            | ✅          | ✅                   | ✅         |\n| [React (Python)](https://github.com/darren277/RESTettaStone/tree/master/frontend/pythonreactapp) | ✅          | ✅                   | ✅         |\n| [React Fiber](https://github.com/darren277/RESTettaStone/tree/master/frontend/reactfiberapp) | ✅          | ✅                   | ✅         |\n| [Svelte](https://github.com/darren277/RESTettaStone/tree/master/frontend/svelteapp)          | ✅          | ✅                   | ✅         |\n| [Vue](https://github.com/darren277/RESTettaStone/tree/master/frontend/vueapp)                | ✅          | ✅                   | ✅         |\n| [Yew (Rust)](https://github.com/darren277/wasm-frp)**                                       | ✅          | ✅**                 | ❌         |\n\n(**) The Yew app is in a separate repo. It does not currently connect to *this* backend, but it integrates just fine with its own backend web server built in Rust, with a SurrealDB database.\n\n### Other\n\n|                                                                                             | Dockerized | Backend Integration | Full CRUD |\n|---------------------------------------------------------------------------------------------|-----------|---------------------|-----------|\n| [Electron](https://github.com/darren277/RESTettaStone/tree/master/other/electronapp)        | N/A       | ✅                   | ✅         |\n| [Expo / React Native](https://github.com/darren277/RESTettaStone/tree/master/other/expoapp) | N/A       | ✅                   | ✅         |\n| [Flutter](https://github.com/darren277/RESTettaStone/tree/master/other/flutterapp)          | N/A       | ✅                   | ✅         |\n| [Chalice](https://github.com/darren277/RESTettaStone/tree/master/other/chaliceapp)          | N/A       | N/A                 | ✅          |\n| [Monitoring](https://github.com/darren277/RESTettaStone/tree/master/other/monitoring)       | ✅         | ✅                   | N/A          |\n| [gRPC](https://github.com/darren277/RESTettaStone/tree/master/other/grpc)                   | ✅         | N/A                 | ✅          |\n| [RabbitMQ](#rabbitmq)                                                                       | ✅         | N/A                 | N/A          |\n\n### Servers\n\n|                                                                                      | Dockerized | Backend Integration | Frontend Integration |\n|--------------------------------------------------------------------------------------|------------|---------------------|----------------------|\n| [Nginx](https://github.com/darren277/RESTettaStone/tree/master/server/nginx)         | ✅          | ✅                   | ✅                    |\n| [Apache Httpd](https://github.com/darren277/RESTettaStone/tree/master/server/apache) | ✅          | ✅                   | ✅                    |\n\n## Standing on the Shoulders of Giants\n\nIn many cases, I made use of some existing projects out there as templates or boilerplate. I wound up modifying many of the Dockerfiles I found, for example, quite substantially, to suit my custom needs. There were also a few repositories I found on GitHub that helped guide my initial efforts for some of the languages where I significantly lacked direct experience.\n\nOne major example of this was the [COBOL backend](https://github.com/azac/cobol-on-wheelchair). I'd have had no idea where to even begin for something like that (well, other than the language specifications and documentation, I suppose - but that would have been unecessarily time consuming, and why reinvent the wheel?).\n\n## Future\n\nThe first immediate steps to carry out will be gradually adding each subdirectory as I test them and maybe clean them up a bit by removing unecessary comments and so on.\n\n## See Also\n\nWhat follows in this section are some other projects I have worked on that are at various stages of completion. They relate to this one in that they explore other aspects of web and desktop application development that do not necessarily fit directly into this repository, or are simply much bigger projects and deserve to stand in their own right.\n\n### Theoretical\n\n* [Series of tubes](https://github.com/darren277/series-of-tubes) is a repository where I analyze the underlying structure of Internet protocols and various constituent parts that compose the various kinds of Internet traffic that make up the World Wide Web. It is a work in progress with many more parts to come.\n* [byodb](https://github.com/darren277/byodb) is an implementation of a SQLite like database from the ground up in C. It is based on a tutorial referenced inside the repository.\n\n### Practical\n\n* [Python to JS/JSX Transpiler](https://github.com/darren277/Transpiler) is a project for transpiling Python code into either JavaScript or JSX code. Theoretically, it could potentially be leveraged to write entire React applications in Python.\n* [Wasm-FRP](https://github.com/darren277/wasm-frp) is a project that uses Rust at every level of the stack.\n  - The back end uses Tokio to construct a REST based web server.\n  - The front end uses Yew to construct a WebAssembly based front end that is compiled and then served client side.\n  - The database it interacts with is even a Rust based technology called SurrealDB. It is a multimodal database with a lot of fun potential.\n\n# How to Use\n\n## Configuring Environment Variables\n\n### `.env` File\n\nYou will need a `.env` file in the root directory with quite a few custom defined environment variables.\n\nThe universal environment variables for the central control mechanism(s) would look something like this:\n\n```sh\nPG_USER=someusername\nPG_PASS=somepassword\n\nNGINX_VERSION=1.17.3\nNGINX_IP=172.18.0.20\nNGINX_PORT=8084\nLUA_JIT_VERSION=2.0.5\n\nSUBNET_NAME=restetta\nSUBNET_CIDR=172.18.0.0/16\n\nPG_IP=172.18.0.21\nPG_HOST=172.18.0.21\nPG_PORT=5432\nPG_DB=postgres\n\nREDIS_IP=172.18.0.19\nREDIS_PORT=6379\n```\n\nAnd then individual environment variables for each subproject would look something like this:\n\n```sh\nCROWAPP_IP=172.18.0.22\nCROWAPP_PORT=18080\n```\n\nPlease note that a lot of these are quite arbitrary. The CIDR subnet that you use, and the IP addresses for each Docker container can almost be randomly generated. The same applies to the ports.\n\n### Setting Environment Variables in Redis and Nginx\n\nThe Nginx configuration process here currently requires knowledge of each container's IP address and port.\n\nYou'll notice the following command in the `Makefile`:\n\n```sh\nredis-set-variable:\n    docker exec -it redis_container redis-cli set $(REDIS_KEY) $(REDIS_VALUE)\n```\n\nOn Windows (for example), you simply need to open a terminal and execute the following type of command (using a particular illustrative example):\n\n```shell\n$env:REDIS_KEY='CROWAPP_PORT'; $env:REDIS_VALUE='18080'; make redis-set-variable\n```\n\nTake note, as well, of the following command inside the `server/nginx/entrypoint.sh` script:\n\n```sh\nenvsubst '${NGINX_PORT},${CROWAPP_IP},${CROWAPP_PORT}' \u003c /usr/local/openresty/nginx/conf/nginx.conf.template \u003e /usr/local/openresty/nginx/conf/nginx.conf\n```\n\nI will be adding each environment variable pair (IP and port) for each subproject as I add them to the repository.\n\nThis will, of course, become rather long and cumbersome over time, so I will likely implement a more elegant solution in the future.\n\n## Using the Makefile\n\nThe Makefile, which requires installation of `make` if you do not already have it, is provided to facilitate the execution of many of the commands needed to run the different pieces of this project.\n\n## Using Docker Compose\n\n**NOTE**: I've abandoned this route as I absolutely refuse to hardcode the build args directly inside of the `docker-compose.yml` file.\n\nFor a project of this size, with dozens of subprojects each with their own `Docker` container specifications, it just doesn't make sense if Docker Compose won't accommodate dynamic build arg interpolation.\n\nSee also: [The build arg gotcha](#docker-compose-gotchas).\n\nFor the sake of posterity, I still include the existing `docker-compose.yml` file in the root directory for anyone who would like to build upon it.\n\nIf you'd like to try using the `docker-compose.yml` file, you can do so by running the following command from the CLI:\n\n```shell\ndocker-compose up\n```\n\nTwo things to note:\n1. I have not yet added every single subproject to the `docker-compose.yml` file. This would be rather tedious, very repetitive, and essentially unnecessary for the time being. The `Makefile` and each individual `Dockerfile` do the job just fine.\n2. Since the YAML keys cannot be interpolated with environment variables, I had to hardcode the network name which bothers me in an OCD kind of way. It's obviously not a huge deal, but it irks me just a smidge.\n\nSee also: [Build args gotcha](#docker-compose-gotchas).\n\n### Overview of Commands\n\n#### Docker\n\nMost of the commands in the Makefile(s) are calling the `docker` CLI tool.\n\nThis includes tasks such as creating the network, building images from individual `Dockerfile`s, running these Docker images, and, in some cases, interacting with them.\n\nI also recently separated the original single `Makefile` into two separate files (with a third to come - namely, `Makefile.frontend`).\n\nTo run a command from the main central `Makefile`, you simply enter something like the following into the terminal:\n\n```shell\nmake docker-subnet\nmake docker-psql\nmake nginx-build\nmake nginx-run\n```\n\nAs for the `Makefile.backend`, the recurring pattern looks as follows:\n\n```shell\nmake b actixapp-build\nmake b actixapp-run\n```\n\nOr:\n\n```shell\nmake b cobolapp-build\nmake b cobolapp-run\n```\n\nNote that the `b` is a shorthand I created to simplify referencing the `Makefile.backend` file.\n\nDon't forget to define your environment variables in a `.env` file, as they are used by these `Makefile` commands. See above section on environment variables for details.\n\n## Testing\n\n### Simple Endpoint Testing with `Make`\n\nI created a Dockerfile for a new container that can be used for testing endpoints.\n\nI've also included one example usage:\n```shell\ncrowapp-test:\n\tdocker exec -it debugger curl http://$(CROWAPP_IP):$(CROWAPP_PORT)/users\n```\n\nThe next thing I'd like to add to this generalized debugger container is a logging system that can create logs either on the host machine or at least be very easily accessible via another `Make` command.\n\nOr, better yet, perhaps create some kind of visual user interface for viewing these logs (or leveraging something like `Kibana` or `Grafana`).\n\n### Using Python Script for More Advanced Testing\n\nI've also included a [Python script](https://github.com/darren277/RESTettaStone/blob/master/other/tests/main.py) that can be used to define some more advanced test cases, including testing for specifics about the returned content.\n\nThis script also allows you to test every container with the same command:\n```shell\nmake run-tests\n```\n\n## Performance Testing with Locust\n\nI've added a container definition and Locust file and configuration options for performance testing.\n\nThere are `make` commands in the `Makefile`:\n\n```shell\nlocust-build:\n\tcd other/performance \u0026\u0026 docker build --build-arg TARGET_HOST=$(TARGET_HOST) --build-arg TARGET_PORT=$(TARGET_PORT) -t locust-$(NAME):1 .\n\nlocust-run:\n\tdocker run -d -it --rm -p 8089:8089 --name locust-$(NAME) --network $(SUBNET_NAME) locust-$(NAME):1\n```\n\nYou can manually run the `docker build` command as follows:\n```shell\nlocust-build NAME=crowapp TARGET_HOST=127.420.69.42 TARGET_PORT=2024\n```\n\nAnd then run the container with the following command:\n```shell\nlocust-run NAME=crowapp\n```\n\n**_Or_**, more preferably... You will likely want to define your own tailored `Make` commands, following the given Flask example already in the `Makefile`:\n\n```shell\nlocust-build-flask:\n\t$(MAKE) locust-build NAME=flask TARGET_HOST=$(FLASKAPP_IP) TARGET_PORT=$(FLASKAPP_PORT)\n\nlocust-run-flask:\n\t$(MAKE) locust-run NAME=flask\n```\n\nI may add a separate `Makefile` in the future that is defined to calling all the different performance testing configurations.\n\nIn the meantime, however, I will leave that as an exercise for the reader.\n\n## Monitoring\n\n### Prometheus and Grafana\n\nThere is a `docker-compose.yml` file in the `other/monitoring` directory that can be used to start a Prometheus monitoring and alerting system with Grafana configured for some very simple dashboard visuals.\n\nTo access the Prometheus dashboard, navigate to `http://localhost:9090`.\n\nTo access the Grafana dashboard, navigate to `http://localhost:3050`.\n\nThe username is `admin`, and the password is set as an environment variable in the root `.env` file as `GF_SECURITY_ADMIN_PASSWORD`.\n\nIt is currently connected to the `flaskapp` Flask back end. I will leave connecting it to other sub projects as an exercise for the reader.\n\nNote: Unlike the root project and it's `Makefile` configurations, the ports and other configuration values are currently hardcoded, which I don't particularly like, but I'll likely revisit that later. Also be aware that, currently, the volume mount for the Prometheus container is the same as the source folder, so it currently modifies the source `prometheus.yml` file each time it's run. This is not ideal, but it's another thing I'm leaving for another day.\n\n## Cloud Deployment\n\n### AWS\n\n#### EC2\n\nThis procedure is going to be making a lot of assumptions regarding preparatory work on the part of the reader. There are lots of resources out there on how to do these things, so I won't add to the noise out there with my own take on an oft repeated process.\n1. Sign up for an AWS account.\n2. Provision an EC2 instance that is within your budget.\n3. Install and configure Docker on this EC2 instance.\n4. Add all required environment variables to your `.env` file (see `Makefile` for reference).\n5. Install the `aws` CLI tool locally.\n6. Configure the `aws` CLI tool with your AWS credentials.\n7. Install and configure the necessary SSH tools on your local machine.\n8. Obtain the right private key file for your EC2 instance security group and point to it using `PEM_KEY_PATH` in the `.env` file.\n9. Ensure that Postgres is installed and running on the EC2 instance `sudo apt install postgresql`.\n10. Ensure that you have Python installed on the EC2 instance `sudo apt install python3`.\n11. And also ensure you have the `python3-dev` package installed `sudo apt install python3-all-dev`.\n12. And yet another prerequisite for Postgres is `sudo apt install libpq-dev`.\n\nAn alternative to the last four steps would be to slightly modify the `populate_pg_db.py` script to run locally while connecting to the remote Dockerized Postgres database. I'll leave this as an exercise for the reader for the time being.\n\n##### Some Docker Notes\n\n###### Docker Permissions\n\nIf you face any Docker permissions issues right from the get go, there are a few ways to go about resolving that.\n\nThe method I went with was: `sudo chown $USER /var/run/docker.sock`.\n\nBe warned that this particular method carries some degree of security risk.\n\nTo test: `docker run hello-world`.\n\nIt may require a `reboot`, but if `reboot` is not ideal, you can restart the Docker service with `sudo systemctl restart docker`, or if that doesn't work, use `sudo systemctl list-units --type=service` to list the services and find the Docker service name. In my case, it was `snap.docker.dockerd.service` so I had to run `sudo systemctl restart snap.docker.dockerd.service`.\n\nSee also: https://stackoverflow.com/questions/48957195/how-to-fix-docker-got-permission-denied-issue\n\n###### Testing Endpoint with Curl\n\nThis step also has a couple of prerequisites:\n1. You must of course have `curl` installed locally.\n2. You must have the IP address or hostname of your EC2 instance stored in your `.env` file as `EC2_HOST`.\n3. You must also have the desired port number allowed access via the security group settings for that EC2 instance (configured via AWS Console, or from the CLI if you're familiar with the process).\n\n##### Some Notes About Make and SSH Commands\n\nThere are likely better equipped tools out there for accomplishing a lot of what I am doing with a `Makefile` and a few SSH related commands.\n\nAnsible, although my experience with it is limited so far, sounds like it might be a good fit for this kind of thing. I'll be exploring that in a future iteration.\n\n##### SSH Keys\n\n###### Key File Permissions\n\nIf you happen to encounter something like the following:\n```\nThe authenticity of host 'ec2-X-X-X-X.compute-1.amazonaws.com (X.X.X.X)' can't be established.\nED25519 key fingerprint is SHA256:rZEy5qUlf9lEj3pDN6b2mWx9XEzilKDkeykF/tpuZKL.\nThis key is not known by any other names.\nAre you sure you want to continue connecting (yes/no/[fingerprint])?\nWarning: Permanently added 'ec2-X-X-X-X.compute-1.amazonaws.com' (ED25519) to the list of known hosts.\nBad permissions. Try removing permissions for user: NT AUTHORITY\\\\Authenticated Users (S-1-5-11) on file C:/Users/Darren/.ssh/my_key.ppk.\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\nPermissions for 'C:/Users/Darren/.ssh/my_key.ppk' are too open.\nIt is required that your private key files are NOT accessible by others.\nThis private key will be ignored.\nLoad key \"C:/Users/Darren/.ssh/publicwebsite.ppk\": bad permissions\nubuntu@ec2-X-X-X-X.compute-1.amazonaws.com: Permission denied (publickey).\nC:\\Windows\\System32\\OpenSSH\\scp.exe: Connection closed\n```\n\nYou may have to modify the permissions for the file to be more exclusive to a particular user.\n\nThere is a discussion with some suggestions and guidance on how to do that here: https://stackoverflow.com/questions/49926386/openssh-windows-bad-owner-or-permissions\n\nJust keep in mind the usual caveats when it comes to fiddling with file permissions.\n\n###### Key Format\n\nThe key format needed for the `Makefile` commands that use SSH from the command line need to be in the following format:\n```shell\n-----BEGIN RSA PRIVATE KEY-----\nUAixppRe3O2ib7z7R9bsYFAjnMG+hMJ4qxTSZUcNaa0rzPnEtmNeCdprl6Ta2f/2t8+l3TYRthXyI\n+TBwFYZ0l/Bo5dJ1J9uZ5wXy94ce0TdCZIlPClYP7xXvGnf23OsNgsV2aZf3sr1llgLTepAbRjuXU\nK5tyzRaAYH+O4S3+D9t3guGyFMZlF8mZNux4LrcRYMn0czmA3gjS23Fj7IFhZtaBO95jw2Mh3cRYl\nqiiqGtt/+j1lsqDvztwpxdrE/jWMnXLDMptWq/BCyVYp1uzIBm0gRwGvXsv+yj+OFhJ9l84P2vKCM\ndu5HW3mdIlX5ivpdepPWENvi3ubzrW80fzcs3idHMqoXVguf+go/nS2FwFaJLUhm55IDr0EmB8hZB\nb8JGRlX8zSnkOXUQb7vlmk4PpoAINbEzedKVEct06Gt7vGbNRNnyha3h0sW6N3FXYyQjeANwHHxNU\n1z1yWWkWUlnne8h5N3B52mNjBZgXI6JhWNxgTvUWM5GmsxPHeciqS7sXYyyNUA+Nlad2fDiBOEirC\nz7bpU9hVgEVNXnYBzzhgMHe44lTV8rwRXK28JCsdmO00wunxrDvkGKfznwqqCWFHEBCUEzsXYDetx\nt+iQsvG0kY7dAgkwvR/b+FiW03OVbap4IK3UWkYJgwwiy1Qu3iPGBTFE3KzM6HSPRTEfH2K5GKjD1\nTuCK4SU/H75c4imbFExawWa1w2o69PdxOU8Gv3f4nwfEwN4bNgkpNajBmLyII2pq4j8I9G+vLQUso\nEnL7XrQzkQse99YYf8FGQsZG/umgRc7Ggxzjfsj34GWehnupE4Q3b5hJNh0qC6F919Q9NJqJ9y3hh\nXEOMsmAeGC3XxMxoByn2cG+G/7UQnCamN7vzru9qKo37vwcPhOu45CIpml88bZ9CUa4AKpjLXuEym\naXU5bC8mlK0ZcCSTN/WvcuM1bXo/uMx8O8RIzf6ilbtQ6nzMiNVx0tTh4YFGRX2CtrKIzxQIRqrLp\nzz/uPGvR1w7YbRFaUSmqSTqBdbetpvszo6qld/7dK2rT/LAF4oUAzD130wvD8TnxbY5U4kkFgo51y\nZUoeKrlXdRZgFa26EQjV5GYVjsciVb9kjcbjAYRaIN/ErcEycliFvFblEL7D2WSUKMybJh8y7ZuxG\n6G8UE8X3DPbWDqZ7WBlz8GHgnRATID24GpCYZlW3sTuBiHfRasvhijA8w/5tjN+71cdrF977gmEyD\n3MRlDEXT9ZxT9SHEjsWD/AlPf7UGyvzAP0M5r70WMeQROXV1oI8Ilk5yqVKGmAvbrgBiVOvy6zerh\n1d9bZVNsim0IrsdqtgG5DTH5gXDZ+aNOKIKCYAvFIuSQoDufxNr4j88XaO6D21D0dSsaHCug1a3pm\nQOAgxGEsvTh6nlXYK72ZNXkYYCiZiYM3+ukhdDSPGnpXkY6tmh9pNbYvmM4kAYvyfh8dfClImG/HA\nsii3ECAGfZE3rNddvuyhUdR6FyOduKMHyp7MvMF5u2QO9h0oG/nBKw5HgYCDl3g8WLlhqq50Lq9iy\nIc5iLbk1/Bon5VKG8LOs/MJyIFA8TnpEgUZ2afd38ZwpPpckXitTmXtFFtgze26LGqOpiKB=\n-----END RSA PRIVATE KEY-----\n```\n\nNot this format:\n```shell\nPuTTY-User-Key-File-2: ssh-rsa\nEncryption: none\nComment: imported-openssh-key\nPublic-Lines: 6\n7FQOxsna4r3cvUluR3IlvYYmcblZcO06deS0auu3j5xTBr2G3+JrFpxsPPVLWR6lJ\njUhAcjsfJuUeToeCIubdrVWX4kbFvfjLm0ZylkJw6lnC1tsugPE6KnjUv4TnOMNZJ\nmxPXoQUr/X22gHuQgcycG65jgwZ9NS000YW4F22DcUHvgibiwytPYThberPczi8zo\nQSKi0HSQ6OgoDfzy5o7Lj8HB5cyS4HIQ8t6ckiPv9EXQMoqGbvEjHJJqBOJYX1uO8\nNG6MWBrZEy5qUlf9lEj3pDNCLGK6b2mWx9XEzilKDkekvyykFL/tpuZKLfAPDUtF5\nyJLAUpcGEj96/RUdYvCvqONzDSllTOq5iRPBIVIpqTqfeqU8f+vx\nPrivate-Lines: 14\ncOwOdky2eprrY5HmskdNERB0Q3G8AFb+fAwCnv8ijUddw/PtLrpthFflaxoSWfubT\n70NzZ5Q85He6o/b4ciEMVVor5+P+NrzBMYap3MigAykBEkW9eN/v//WnZ8C/YaNhs\nXwq8dCJA4uWVnWYPRuPjI+n56IPYw+0Yf/eWQsES+2CO8Ut9A9gn+i09ziZrRzFOT\n79rdwTVjY77KBqxbL/yjHZ/nEBzXWUT9EI2aXWHdDcPrKGP9K1NpTAef8KKQ4S/0j\npO0Sh9j2BKIDdvMn0xvkXCuxcbp0Hb9IpVQMmjF+Hnp+3EA5MUvGJZMMgCr/Fvhqn\nW/FrFtbYrqdItJMb6aXbBCPQhATPNSu4B0w9QSGvmvJEEtu0Uv2nnKZX2tupVL7tq\nKYLMo/iB/Qcq4y36ANEVfw/t7js47kFCNe5ERPVnR4/vXkhzmRZ7DiuHr6+Y1xWw4\n1G1CVKNJwQZchuQy+cdX2YfFVk6pUCIcsl928zzV/7BSEBfnfRB/0X1iJbkLLoWpc\ndP2v6wI4Ey9eTCFKs7iSvqwhI2DxL0WjwNo9SLZxip8joEQoLUWGaMwTxQ52TjfQB\n0QQ1CtRYYgPt8TZD6wSdnBrm5s1SsZr4L4DDRPDONbbbTCDvpaarS24AqDd++iOS0\nifV4OXD1nzMlSxh6o9zUeF/sXvlHdH+g99K4nyQJnRIG1VkTIYMeghNO2GZkRwVUI\nbxXiJsM6iwXnvtlDLmNyIw7RdLCQQgt1yL72VZzwcATjPy6D8E7cpvXZctLoSDqjV\nZPWzm0TNfBWqbg/t379hgSsU0cI2QnN8684Z6gLOvm4xTUx6LQOsrWQrF0irQCWPD\noyx6vgyLpS37kVhrRnnx093cXzFYzhfSl8RN5zSur/JY/LD=\nPrivate-MAC: f6624c2b7933e85f536d0020fe519821074a415c\n```\n\nThere are conversion tools out there. I believe [PuTTYgen](https://www.puttygen.com) does the trick.\n\nBy the way, do not share your private keys in public places. The above two examples are not actual key files of mine, but rather generated with a script to look nearly identical to actual key files of each type.\n\nAnd why would I go to the trouble of writing a script to do such a thing?\n\nWell, [why not](https://github.com/darren277/RESTettaStone/tree/master/other/utils/just_for_fun/keygen.py)?\n\n## RabbitMQ\n\nIn a browser, navigate to http://localhost:8080 and enter `guest` for both the username and password.\n\nThere are some `Makefile` commands for RabbitMQ as well.\n\nTo build and run the RabbitMQ container, you can use the following commands:\n```shell\nmake mq run\n```\n\nTo test the container, run the following command:\n```shell\nmake mq test\n```\n\nTo interact with the RabbitMQ container, there are quite a few commands in the `Makefile`, such as:\n```shell\nmake mq create-queue queue=my_queue\nmake mq publish-message queue=my_queue msg=\"Hello, World!\"\n```\n\n# Lessons\n\n## DevOps Gotchas\n\n### Nginx Gotchas\n\n#### Environment Variables\n\nWhen trying to assign a variable port to the `listen` directive, I discovered that Nginx simply does not allow for that, even when using leveraging lua.\n\nI had to use an `envsubst` call in the Dockerfile as a work around.\n\n### Docker Gotchas\n\n#### ARGs and Multi Stage Builds\n\nI kept ending up with empty strings for my ENV vars when trying to construct variable rich strings. It turns out that build `ARG` values passed in via the Docker CLI are reset with each build when it comes to multi stage builds.\n\nRefer to: https://stackoverflow.com/a/68061457/10973023\n\n#### Exec Form vs Shell Form for ENTRYPOINT:\n\nWhen including ENV vars inside of a `Dockerfile`, it is important to note that `ENTRYPOINT` commands will be unable to interprete these values when written in the `exec form` format.\n\nDoes NOT work (`exec form`): `ENTRYPOINT [\"dotnet\", \"aspnetapp.dll\", \"--urls\", $URL]`\nDOES work (`shell form`): `ENTRYPOINT dotnet aspnetapp.dll --urls $URL`\n\nRefer to: https://stackoverflow.com/questions/37904682/how-do-i-use-docker-environment-variable-in-entrypoint-array\n\n### Docker Compose Gotchas\n\n#### Dynamic Build Args\n\nWow, just wow.\n\nAfter hours of trial and error and revisiting the docs and various StackOverflow discussions, it turns out there's really no way to dynamically populate build args from a file.\n\nThis is incredibly disappointing, as the only workarounds are either:\n1. Passing the build args in manually via the CLI, which of course would be utterly redundant with respect to the `Makefile` protocol I'm already using.\n2. Writing some kind of script to generate the `docker-compose.yml` file, which, again, would simply be completely redundant.\n3. Hardcoding the values in the `docker-compose.yml` file, which I'm just plain not a fan of.\n\nSo I'm pretty much abandoning the Docker compose route here.\n\nFor posterity's sake, I'm going to include the `docker-compose.yml` file that I had started on, even though it won't work unless you use one of the three mechanisms I just mentioned for injecting the build args, which I just refuse to do on principle. But feel free to give it a go if you're up for it.\n\n### Make Gotchas\n\n#### Tabs vs Spaces\n\nMakefiles require tabs and will throw an error if you use spaces instead.\n\nFor instance, the following will throw an error:\n\n**Makefile**:\n```shell\nnginx-v2-build:\n    cd server/nginx-v2 \u0026\u0026 docker build --build-arg NGINX_VERSION=$(NGINX_VERSION) --build-arg LUA_JIT_VERSION=$(LUA_JIT_VERSION) --build-arg ENTRYPOINT_VERSION=2 --build-arg NGINX_PORT=$(NGINX_PORT) -t nginx-v2:latest .\n```\n\n**Error**:\n```shell\n(venv) PS C:\\Users\\Darren\\PycharmProjects\\Miniprojects\\RESTettaStone\u003e make nginx-v2-build\nMakefile:43: *** multiple target patterns.  Stop.\n```\n\nWhereas this next snippet will work just fine:\n\n```shell\nnginx-v2-build:\n\tcd server/nginx-v2 \u0026\u0026 docker build --build-arg NGINX_VERSION=$(NGINX_VERSION) --build-arg LUA_JIT_VERSION=$(LUA_JIT_VERSION) --build-arg ENTRYPOINT_VERSION=2 --build-arg NGINX_PORT=$(NGINX_PORT) -t nginx-v2:latest .\n```\n\nSo Makefiles are picky about this... so what, right?\n\nIt's the completely ambiguous error message that makes this particularly frustrating.\n\nThe message *\"multiple target patterns\"* seems to indicate another root cause altogether, so if you don't already associate that error in your mind with a case of using the wrong whitespace for indentation, it can be perplexing indeed.\n\nJust something to keep in mind.\n\n## Language and Framework Gotchas\n\n### Symfony (PHP)\n\n#### Headers vs Body and the Whitespace Issue\n\nI struggled with the fact that I was receiving a response in plain text while I was setting the `Content-Type` header to `application/json`.\n\nI quadruple checked that the string I was sending was perfectly structured to the satisfaction of the JSON specifications, but it just kept showing up as `text/html`.\n\nIt turned out that there was some extra whitespace finding its way into the response. The solution to that, as it turns out, is to wrap the entire construction of the response inside of a pair of functions that deal with *output buffering* (`ob_start()` and `ob_end_clean()`).\n\nHere is an example of how that looks:\n```php\nclass UserController extends AbstractController\n{\n    #[Route('/users', name: 'users')]\n    public function hello(EntityManagerInterface $entityManager, SerializerInterface $serializer): Response\n    {\n        ob_start(); // Start output buffering\n\n        $repository = $entityManager-\u003egetRepository(User::class);\n\n        $response = new Response();$users = $repository-\u003efindAll();\n        $jsonContent = $serializer-\u003eserialize($users, 'json');\n        $response = new Response($jsonContent, Response::HTTP_OK, ['Content-Type' =\u003e 'application/json',]);\n\n        ob_end_clean(); // End output buffering\n\n        return $response;\n    }\n}\n```\n\n#### CLI Race Condition\n\nI encountered a very bizarre peculiarity with running Symfony in a Docker container that I have not encountered in any of the other subprojects.\n\nAfter a lot of trial and error, it eventually came down to adding a 5 second delay to wait for the application to be fully up and running.\n\nThe following `ENTRYPOINT` command resulted in the container exiting immediately:\n```shell\nENTRYPOINT symfony serve --port=$PORT\n```\n\nWhereas, during my debugging, I was able to get it to work just fine by replacing that line with `ENTRYPOINT bash` and then accessing the internal CLI and entering `symfony serve --port=$PORT`.\n\nUltimately, the following `ENRTYPOINT` ended up working:\n```shell\nENTRYPOINT bash -c \"sleep 5 \u0026\u0026 symfony serve --port=${PORT}\"\n```\n\nA note on one of the techniques I was able to use to help ensure at least that the application was executing successfully and not throwing any errors was the following command:\n```shell\ndocker run -it symfony_app:1 /bin/bash\n```\n\nThis super helpful command will likely come in handy in the future when I'm looking to troubleshoot containers that close immediately.\n\n### Rocket (Rust)\n\n#### Pear Codegen Dependency\n\nI encountered a somewhat silly error when trying to build the Rocket app. The message was something to the effect of \"failed to run custom build command for `pear_codegen v0.1.5`\".\n\nI had tried explicitly specifying a newer version of `pear_codegen` in my `Cargo.toml` configuration file, but it was a dependency of a dependency, so that might be why I was unable to override it.\n\nInterestingly, others have encounters problems with this exact scenario going back about six years: https://github.com/SergioBenitez/Pear/issues/13\n\nThe solution that ultimately worked was adding the following command to the `Dockerfile` (even though I explicity define the Rust version to be `nightly` in several places):\n```shell\nRUN /usr/local/cargo/bin/rustup override set nightly\n```\n\n### Django (Python)\n\n#### Project Structure\n\nI'll be honest, I struggled a bit with Django. I had worked with the framework many years ago, so it has been a while. There's that, and then the fact that I have a general preference for working with frameworks that are a lot less rigidly structured. I will always prefer Flask over Django, for example.\n\nIn order to use the framework, one is instructed that they must run a couple of code generation steps, and then modify the generated boilerplate as needed. Well, I kind of skipped that step, to make it more like working with Flask (where you build from bottom up). This led to some initial difficulties while I was getting to know exactly how the files are meant to be named and structured throughout.\n\n#### URL Routing\n\nI faced similar challenges with URL routing as I did with the project structure.\n\nThis might have been exacerbated by my Docker container networking and Nginx configurations for this particular project. For example, I disabled i18n in the `settings.py` to avoid any `en` prefixes.\n\n#### Django REST Framework\n\nIt is pretty clear that Django proper is meant to be server HTML files (in the form of templates). This particular use case here though is REST endpoints exclusively (for the backend portion, at least).\n\nFor these reasons, I'd likely go with the official Django REST framework over traditional Django for such use cases.\n\nIn fact, I may add such a subproject to the ever-growing collection here.\n\n### Zig\n\n#### Postgres Connection Pool Inconsistency Problem\n\nFull CRUD works, however, there is a problem with it behaving inconsistently and sometimes throwing errors about the database being closed.\n\nI'm leaving this as a problem to solve in the future.\n\n### Fat Free (PHP)\n\n#### Dependency Source\n\nSo I had an interesting experience with getting Fat Free to work right. I kept getting 500 errors automatically with every endpoint. It turned out that there was an issue with `E_STRICT` being used but deprecated for the latest versions of PHP. I had the option to downgrade PHP, but didn't really want to go that route.\n\nAnother option I had was to patch the code directly in the downloaded package. In fact, I started adding a few `sed` commands to my `Dockerfile` to accomplish just that:\n```shell\n# Patch the offending line in FatFree framework's base.php\nRUN sed -i 's/error_reporting((E_ALL|E_STRICT)/error_reporting(E_ALL/' vendor/bcosca/fatfree-core/base.php\n```\n\nAnd I was using various `grep` calls to find other issues including a nullable parameter thing:\n```shell\n# grep -Po \"function \\w+\\((.*?)\\)\" vendor/bcosca/fatfree-core/base.php\n\n# Patch the Preview::render() method to fix implicit nullable parameter\n# Patch the functions referencing $hive\nRUN sed -i 's/array $hive=NULL/?array $hive=NULL/g' vendor/bcosca/fatfree-core/base.php\n```\n\nBut I also kept wondering how these simple fixes hadn't already been done by the maintainers of the repo.\n\nWell, it turns out that the changes _had been made_. I searched inside of GitHub for the offending bits of code, and they were all fixed!\n\nSo I spend a bit of time just absolutely puzzled as to what was going on. Of course, I double, even triple, checked that the version number specified in my `Dockerfile` and in my `composer.json` file reflected the latest release according to GitHub.\n\nSuddenly, at some point, I noticed that the repository I was searching through on the GitHub.com website was at `f3-factory/fatfree-core`, not `bcosca/fatfree-core`.\n\nI of course remedied that immediately and everything worked!\n\nSo the moral of the story here - the actual \"Gotcha!\" - is to keep an eye out for scenarios where there can be more than one conflicting repository, probably due to a forking or something like that.\n\n## Database Gotchas\n\n### Postgres\n\n#### pg_stat_activity Locks\n\nI encountered my first case of unfinished processes locking my Postgres database.\n\nIn order to determine which processes are causing the locks, you can run the following query:\n```sql\nSELECT * FROM pg_stat_activity;\n```\n\nAnd then to remove the locks, you can run the following query:\n```sql\nREVOKE CONNECT ON DATABASE postgres FROM PUBLIC, myusername;\n\nSELECT \n    pg_terminate_backend(pid) \nFROM \n    pg_stat_activity \nWHERE \n    -- don't kill my own connection!\n    pid \u003c\u003e pg_backend_pid()\n    -- don't kill the connections to other databases\n    AND datname = 'postgres'\n    ;\n```\n\nFor some additional background:\n* https://stackoverflow.com/questions/48783188/postgres-table-queries-not-responding\n* https://stackoverflow.com/questions/17654033/how-to-use-pg-stat-activity\n* https://stackoverflow.com/questions/5108876/kill-a-postgresql-session-connection\n\nAs for what specifically kept causing the locks in the first place, I have a few theories, but I did not test any of them out directly (there were over 20 different locks by the time I noticed they were causing problems).\n\nThey did occur when I started adding database interaction during the running of the [full scale CRUD testing suites](#using-python-script-for-more-advanced-testing).\n\n## Other General Lessons\n\n### Branch Protection\n\nWell, a little over 600 commits in with this project and I slipped up and pushed three commits directly to the main branch before realizing that I hadn't branched off. [The first one](https://github.com/darren277/RESTettaStone/commit/1a861861a9cbfec420ce9dec1e4c411d1772cc1f).\n\nOn work projects, if there aren't already protections in place, I always insist on them, because mistakes can happen rather easily. I guess I kind of figured I wouldn't make that type of slip up, but, alas, I did.\n\nI of course went right ahead and added some branch protection rules for the main branch, and I advise anyone else out there to do the same, whether it's a professional repo or just a fun little side project like this, it's best to follow good practices with consistency.\n\n## Comparisons\n\n### Projects by Language\n\nPlease note that some of these categorizations are a bit arbitrary and open to debate. I simply wanted to cluster them for readability sake (refer to the concept of [\"chunking\"](https://en.wikipedia.org/wiki/Chunking_(psychology)) in cognitive psychology research into working memory).\n\n#### Classic\n\nSee also: [Performant](#performant)\n\n* **C**: See [Series of Tubes repo](https://github.com/darren277/series-of-tubes).\n* **C++**:\n  * [Crow](https://github.com/darren277/RESTettaStone/tree/master/backend/crowapp).\n\n#### Performant\n\nSee also: [C and C++](#classic)\n\nThese are the languages frequently used for compiled system level applications, where memory management and type safety are of critical concern.\n\n* **Rust**:\n  * [Actix](https://github.com/darren277/RESTettaStone/tree/master/backend/actixapp).\n  * [Rocket](https://github.com/darren277/RESTettaStone/tree/master/backend/rocketapp).\n  * See also: [Wasm-FRP repo](https://github.com/darren277/wasm-frp).\n  * Yew (Front end framework): See [Wasm-FRP repo](https://github.com/darren277/wasm-frp).\n* **Zig**:\n  * [Zig](https://github.com/darren277/RESTettaStone/tree/master/backend/zigapp).\n* **Go**:\n  * [Go](https://github.com/darren277/RESTettaStone/tree/master/backend/goapp).\n* **D**:\n  * [Vibe](https://github.com/darren277/RESTettaStone/tree/master/backend/vibeapp).\n\n#### Object Oriented\n\nThis category is pretty straight forward. These are the primarily object-oriented languages.\n\n(PS: As a personal aside, these are by far my least favorite languages to work with. I frequently refer to them as the boilerplate languages as it takes so many lines of code to write the equivalent to other languages. And it's not even that I dislike object oriented programming. Python, JavaScript, and Ruby also allow for OOP without all the excessive boilerplate. See also: The [discussion regarding language simplicity](#simplicity))\n\n* **C#**:\n  * [Asp.Net](https://github.com/darren277/RESTettaStone/tree/master/backend/aspnetapp).\n* **Java**:\n  * [SpringBoot](https://github.com/darren277/RESTettaStone/tree/master/backend/springbootapp).\n  * [Tomcat](https://github.com/darren277/RESTettaStone/tree/master/backend/tomcatapp).\n\n#### Procedural\n\n* **Fortran**:\n  * (Still incoming) [Fortran](https://github.com/darren277/RESTettaStone/tree/master/backend/fortranapp).\n* **Pascal**:\n  * (Still incoming) [Pascal](https://github.com/darren277/RESTettaStone/tree/master/backend/pascalapp).\n* **COBOL**:\n  * (Still incoming) [COBOL](https://github.com/darren277/RESTettaStone/tree/master/backend/cobolapp).\n\n#### Functional\n\nThese languages are the prototypical functional programming languages.\n\n* **Lisp**:\n  * (Still incoming) [Lisp](https://github.com/darren277/RESTettaStone/tree/master/backend/lispapp).\n* **Haskell**:\n  * (Still incoming) [Haskell](https://github.com/darren277/RESTettaStone/tree/master/backend/haskellapp).\n  * (Still incoming) [Spock](https://github.com/darren277/RESTettaStone/tree/master/backend/spockapp).\n\n#### Multi-Paradigm\n\nWith these languages, you get to have the best of both worlds. You can write object-oriented code, or you can write functional code. Or you can mix the two.\n\n* **Scala**:\n  * [Play](https://github.com/darren277/RESTettaStone/tree/master/backend/playapp).\n* **Swift**:\n  * [Swift](https://github.com/darren277/RESTettaStone/tree/master/backend/swiftapp).\n* **F#**:\n  * [F#](https://github.com/darren277/RESTettaStone/tree/master/backend/fsharpapp).\n\n#### Versatile or Other\n\nThis category is almost a catch all of sorts. These are languages that can be written with different paradigms in mind, perhaps have loose typing, and are generally more flexible. While I created a dedicated section for Scala, Swift, and F# as the \"multi-paradigm\" languages, they could just as easily fit into this cluster. However, [as mentioned above](#projects-by-language), I am categorizing these mostly for the sake of readability.\n\nSome of these languages are frequently categorized as \"scripting languages.\" I'm a not a big fan of that particular characterization as it gives the impression that they are less capable than the others, when in fact, their versatility makes them that much more powerful. They are all Turing complete languages, so if you find them particularly limiting, that's just a skill issue.\n\nAnd then there's Prolog. Prolog is a truly unique language as it falls under the rare category of logic programming.\n\nNote that I consider [JavaScript](#javascript) to fall into this category as well, but it gets its own section because there are so many frameworks built for it.\n\n* **Lua**:\n  * [Lua](https://github.com/darren277/RESTettaStone/tree/master/backend/luaapp).\n* **Perl**:\n  * [Perl](https://github.com/darren277/RESTettaStone/tree/master/backend/perlapp).\n* **PHP**:\n  * [Fat Free](https://github.com/darren277/RESTettaStone/tree/master/backend/fatfreeapp).\n  * [Laravel](https://github.com/darren277/RESTettaStone/tree/master/backend/laravelapp).\n  * [Symfony](https://github.com/darren277/RESTettaStone/tree/master/backend/symfonyapp).\n* **Python**:\n  * [Django](https://github.com/darren277/RESTettaStone/tree/master/backend/djangoapp).\n  * [Flask](https://github.com/darren277/RESTettaStone/tree/master/backend/flaskapp).\n  * (Still incoming) [Chalice](https://github.com/darren277/RESTettaStone/tree/master/other/chaliceapp).\n* **Ruby**:\n  * [Rails](https://github.com/darren277/RESTettaStone/tree/master/backend/railsapp).\n* **Prolog**:\n  * [Prolog](https://github.com/darren277/RESTettaStone/tree/master/backend/prologapp).\n\n#### JavaScript\n\nThis language gets its own category simply because it is so frequently used in front end frameworks.\n\n* **Back End JS**:\n  * [Bun](https://github.com/darren277/RESTettaStone/tree/master/backend/bunapp).\n  * [Firebase](https://github.com/darren277/RESTettaStone/tree/master/backend/firebaseapp).\n  * [Node](https://github.com/darren277/RESTettaStone/tree/master/backend/nodeapp).\n* **Front End JS**:\n  * [Angular](https://github.com/darren277/RESTettaStone/tree/master/frontend/angularapp).\n  * [Gatsby](https://github.com/darren277/RESTettaStone/tree/master/frontend/gatsbyapp).\n  * [Next](https://github.com/darren277/RESTettaStone/tree/master/frontend/nextapp).\n  * [React](https://github.com/darren277/RESTettaStone/tree/master/frontend/reactapp).\n  * [React Fiber](https://github.com/darren277/RESTettaStone/tree/master/frontend/reactfiberapp).\n  * [Vue](https://github.com/darren277/RESTettaStone/tree/master/frontend/vueapp).\n  * [Expo / React Native](https://github.com/darren277/RESTettaStone/tree/master/other/expoapp).\n  * [Electron](https://github.com/darren277/RESTettaStone/tree/master/other/electronapp).\n\n### Metrics\n\n**NOTE**: **This section is a work in progress.**\n\nThis is where I will discuss the various trade offs between the different languages and frameworks.\n\n#### Qualitative\n\n##### Simplicity\n\nThis is where I will discuss the trade-off between simplicity and complexity of code (from a human readability standpoint, not in terms of, say, cyclic complexity, which is a more quantitative measure). This includes factors such as intuitiveness, ease of use, and the number of lines of code needed to accomplish a given task.\n\n#### Quantitative\n\n##### Performance\n\nTBD...\n\n#### Both Qualitative and Quantitative\n\n##### Security\n\nTBD...\n\n##### Safety\n\nType safety, memory safety, etc.\n\n# Optimization\n\n## Latency\n\nTBD...\n\n## Docker Image Size\n\n### Table Sorted Alphabetically\n\n| App | Image Size  |\n|-----------|-------------|\n|\tactix_app\t| \t1.88 GB\t   |\n|\tangular_app\t| \t1.88 GB\t   |\n|\taspnet_app\t| \t223.67 MB\t |\n|\tbun_app\t| \t226.27 MB\t |\n|\tclojure_app\t| \t623.15 MB\t |\n|\tcobol_app\t| \t243.88 MB\t |\n|\tcrow_app\t| \t932.74 MB\t |\n|\tdebugger\t| \t11.05 MB\t  |\n|\tdjango_app\t| \t1.07 GB\t   |\n|\tfatfree_app\t| \t618.25 MB\t |\n|\tfirebase_app\t| \t1.84 GB\t   |\n|\tflask_app\t| 1.03 GB\t    |\n|\tfortran_app\t| \t508.85 MB\t |\n|\tfsharp_app\t| \t1.12 GB\t   |\n|\tgatsby_app\t| \t1.59 GB\t   |\n|\tgo_app\t| \t852.56 MB\t |\n|\tgrpc_client\t| \t1.07 GB\t   |\n|\tgrpc_server\t| \t1.4 GB\t    |\n|\thaskell_app\t| \t8.12 GB\t   |\n|\tlaravel_app\t| \t707.13 MB\t |\n|\tlisp_app\t| \t1.26 GB\t   |\n|\tlua_app\t| \t413.29 MB\t |\n|\tnext_app\t| \t888.96 MB\t |\n|\tnginx\t| \t405.53 MB\t |\n|\tnode_app\t| \t1.13 GB\t   |\n|\tpascal_app\t| \t1.72 GB\t   |\n|\tperl_app\t| \t1.86 GB\t   |\n|\tphp_app\t| \t649.19 MB\t |\n|\tplay_app\t| \t288.35 MB\t |\n|\tpostgres\t| \t377.36 MB\t |\n|\tpostgres\t| \t373.13 MB\t |\n|\tpostgres\t| \t212.85 MB\t |\n|\tprolog_app\t| \t577.73 MB\t |\n|\trails_app\t| \t1.16 GB\t   |\n|\treact_app\t| \t541.67 MB\t |\n|\treactfiber_app\t| \t1.85 GB\t   |\n|\trocket_app\t| \t2.37 GB\t   |\n|\tspock_app\t| \t5.83 GB\t   |\n|\tspringboot_app\t| \t809.8 MB\t  |\n|\tswift_app\t| \t345.7 MB\t  |\n|\tsymfony_app\t| \t4.65 GB\t   |\n|\ttomcat_app\t| \t681.22 MB\t |\n|\tvibe_app\t| \t1.49 GB\t   |\n|\tvue_app\t| \t577.67 MB\t |\n|\tzig_app\t| \t567.78 MB\t |\n\n### Table Sorted by Image Size\n\n| App | Image Size  |\n|-----------|-------------|\n|\thaskell_app\t| \t8.12 GB\t   |\n|\tspock_app\t| \t5.83 GB\t   |\n|\tsymfony_app\t| \t4.65 GB\t   |\n|\trocket_app\t| \t2.37 GB\t   |\n|\tactix_app\t| \t1.88 GB\t   |\n|\tangular_app\t| \t1.88 GB\t   |\n|\tperl_app\t| \t1.86 GB\t   |\n|\treactfiber_app\t| \t1.85 GB\t   |\n|\tfirebase_app\t| \t1.84 GB\t   |\n|\tpascal_app\t| \t1.72 GB\t   |\n|\tgatsby_app\t| \t1.59 GB\t   |\n|\tvibe_app\t| \t1.49 GB\t   |\n|\tgrpc_server\t| \t1.4 GB\t    |\n|\tlisp_app\t| \t1.26 GB\t   |\n|\trails_app\t| \t1.16 GB\t   |\n|\tnode_app\t| \t1.13 GB\t   |\n|\tfsharp_app\t| \t1.12 GB\t   |\n|\tgrpc_client\t| \t1.07 GB\t   |\n|\tdjango_app\t| \t1.07 GB\t   |\n|\tflask_app\t| 1.03 GB\t    |\n|\tclojure_app\t| \t623.15 MB\t |\n|\tcrow_app\t| \t932.74 MB\t |\n|\tnext_app\t| \t888.96 MB\t |\n|\tgo_app\t| \t852.56 MB\t |\n|\tspringboot_app\t| \t809.8 MB\t  |\n|\tlaravel_app\t| \t707.13 MB\t |\n|\ttomcat_app\t| \t681.22 MB\t |\n|\tphp_app\t| \t649.19 MB\t |\n|\tfatfree_app\t| \t618.25 MB\t |\n|\tprolog_app\t| \t577.73 MB\t |\n|\treact_app\t| \t541.67 MB\t |\n|\tvue_app\t| \t577.67 MB\t |\n|\tzig_app\t| \t567.78 MB\t |\n|\tfortran_app\t| \t508.85 MB\t |\n|\tlua_app\t| \t413.29 MB\t |\n|\tnginx\t| \t405.53 MB\t |\n|\tpostgres\t| \t377.36 MB\t |\n|\tpostgres\t| \t373.13 MB\t |\n|\tswift_app\t| \t345.7 MB\t  |\n|\tplay_app\t| \t288.35 MB\t |\n|\tcobol_app\t| \t243.88 MB\t |\n|\tbun_app\t| \t226.27 MB\t |\n|\taspnet_app\t| \t223.67 MB\t |\n|\tpostgres\t| \t212.85 MB\t |\n\n### Some Potential Solutions\n\n#### Multi-Stage Builds\n\nTBD...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarren277%2Frestettastone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarren277%2Frestettastone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarren277%2Frestettastone/lists"}