{"id":13714217,"url":"https://github.com/learning-cloud-native-go/myapp","last_synced_at":"2026-01-24T10:11:33.734Z","repository":{"id":39545881,"uuid":"200226511","full_name":"learning-cloud-native-go/myapp","owner":"learning-cloud-native-go","description":"🚀 How to build a Dockerized RESTful API application using Go.","archived":false,"fork":false,"pushed_at":"2024-05-12T11:42:55.000Z","size":69662,"stargazers_count":816,"open_issues_count":1,"forks_count":65,"subscribers_count":19,"default_branch":"main","last_synced_at":"2024-08-03T23:29:58.167Z","etag":null,"topics":["cloud-native","docker","go","golang","kubernetes","microservice","restful-api","tutorial"],"latest_commit_sha":null,"homepage":"https://learning-cloud-native-go.github.io","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/learning-cloud-native-go.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":["dumindu"],"custom":"https://www.buymeacoffee.com/dumindu"}},"created_at":"2019-08-02T11:54:58.000Z","updated_at":"2024-08-03T18:13:45.000Z","dependencies_parsed_at":"2023-02-10T20:30:49.689Z","dependency_job_id":"a28e7573-75d1-4c4c-a1f2-7aaa4c43a2f8","html_url":"https://github.com/learning-cloud-native-go/myapp","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-cloud-native-go%2Fmyapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-cloud-native-go%2Fmyapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-cloud-native-go%2Fmyapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-cloud-native-go%2Fmyapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/learning-cloud-native-go","download_url":"https://codeload.github.com/learning-cloud-native-go/myapp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224551190,"owners_count":17330094,"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":["cloud-native","docker","go","golang","kubernetes","microservice","restful-api","tutorial"],"created_at":"2024-08-02T23:01:54.995Z","updated_at":"2026-01-24T10:11:33.728Z","avatar_url":"https://github.com/learning-cloud-native-go.png","language":"Go","readme":"[![buymeacoffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-dumindu-FFDD00?style=for-the-badge\u0026logo=buymeacoffee\u0026logoColor=ffffff\u0026labelColor=333333)](https://www.buymeacoffee.com/dumindu)\n\n# Learning Cloud Native Go - myapp\n\n## 🔋 Batteries Included\n\n- Use of Go linters, Docker, Docker Compose, Alpine development images, and Distroless production images.\n- Use of [GORM CLI](https://gorm.io/cli/) to generate Go generics-based database repositories.\n- Use of [Goose](https://github.com/pressly/goose) to build a DB migration CLI with embedded migrations.\n- Use of [Validator.v10](https://github.com/go-playground/validator) to validate forms via Go generics-based, fail-fast validation middleware.\n- Use of [Zerolog](https://github.com/rs/zerolog) to generate request logs and centralized Syslog logging.\n- Use of [Swag.v2](https://github.com/swaggo/swag) to generate OpenAPI v3 specifications.\n- Use of GitHub Actions to run linters and tests, and to build and push production images to the registry.\n\n\u003e 💡 Go v1.26rc2 for json/v2 and new compiler features.\n\n| Environment    | Go 1.26rc2 Image Size | Postgres v18 Image Size |\n|----------------|-----------------------|-------------------------|\n| Development    | 777 MB                | 300MB                   |\n| Production     | 30 MB                 |                         |\n\n## 📟 Available Commands\n\n```just\n$ just\n🚀MYAPP\n    help                    # List available commands\n    go-run cmd=\"app\"        # Run a specific cmd (defaults to app)\n    go-run-migrate cmd=\"up\" # Run database migrations (defaults to up)\n    build                   # Run docker compose build\n    up cmd=\"\"               # Run docker compose up\n    down                    # Run docker compose down\n    lint                    # Run lints using gofumpt, go vet, staticcheck and govulncheck\n    test                    # Run tests\n    gen                     # Run go generate for all packages\n    gen-openapi             # Generate openapi v3 specification using swag v2\n    gen-gorm-repos          # Generate gorm repositories using gorm cli\n```\n\n## 🛬 Endpoints\n\n| Name        | HTTP Method | Route          |\n|-------------|-------------|----------------|\n| Health      | GET         | /livez         |\n| List Books  | GET         | /v1/books      |\n| Create Book | POST        | /v1/books      |\n| Read Book   | GET         | /v1/books/{id} |\n| Update Book | PUT         | /v1/books/{id} |\n| Delete Book | DELETE      | /v1/books/{id} |\n\n## 🗄️ Database Design\n\n| Column Name    | Datatype  | Not Null | Primary Key |\n|----------------|-----------|----------|-------------|\n| id             | UUID      | ✅        | ✅           |\n| title          | TEXT      | ✅        |             |\n| author         | TEXT      | ✅        |             |\n| published_date | DATE      | ✅        |             |\n| image_url      | TEXT      |          |             |\n| description    | TEXT      |          |             |\n| created_at     | TIMESTAMP | ✅        |             |\n| updated_at     | TIMESTAMP | ✅        |             |\n\n## ⛔️ Form Validation\n\n```json\n{\n  \"errors\": {\n    \"title\": \"This is a required field\",\n    \"author\": \"This can only contain alphabetic and space characters\",\n    \"published_date\": \"This must be a valid date\",\n    \"image_url\": \"This must be a valid URL\"\n  }\n}\n```\n\n## 📝 Request Logs and Centralized Syslog Logging\n\n```json lines\ndb-1  | 2018-01-10 01:00:00.000 +08 [1] LOG:  database system is ready to accept connections\nContainer myapp-db-1 Healthy\napp-1  | 2018/01/10 01:00:00 OK   00001_create_books_table.sql (2.21ms)\napp-1  | 2018/01/10 01:00:00 goose: successfully migrated database to version: 1\napp-1  |\napp-1  | {\"level\":\"info\",\"time\":\"2018-01-10T02:00:00+08:00\",\"message\":\"Starting server :8080\"}\napp-1  |\napp-1  | [7.218ms] [rows:1] INSERT INTO books (id, created_at, updated_at, title, author, published_date, image_url, description) VALUES ('38ba23d1-9565-40ed-b781-aacd2f84018d', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'Death Note', 'Light Yagami', '2006-10-04 00:00:00', 'https://static.wikia.nocookie.net/deathnote/images/9/94/A_Death_Note.jpg', 'A supernatural volume dropped into the human world by the Shinigami Ryuk') RETURNING *\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mq7oi6hkls7397s43g\",\"received_time\":\"2018-01-10T03:00:00+08:00\",\"method\":\"POST\",\"url\":\"/v1/books\",\"header_size\":135,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":201,\"resp_header_size\":47,\"resp_body_size\":296,\"latency\":8.307,\"time\":\"2018-01-10T03:00:00+08:00\"}\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mq7oi6hkls7397s43g\",\"id\":\"38ba23d1-9565-40ed-b781-aacd2f84018d\",\"time\":\"2018-01-10T03:00:00+08:00\",\"message\":\"new book created\"}\napp-1  |\napp-1  | [2.541ms] [rows:1] SELECT * FROM books WHERE id = '38ba23d1-9565-40ed-b781-aacd2f84018d'\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mqa6a6hkls7397s44g\",\"received_time\":\"2018-01-10T04:00:00+08:00\",\"method\":\"GET\",\"url\":\"/v1/books/38ba23d1-9565-40ed-b781-aacd2f84018d\",\"header_size\":82,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":200,\"resp_header_size\":47,\"resp_body_size\":296,\"latency\":2.674625,\"time\":\"2018-01-10T04:00:00+08:00\"}\napp-1  |\napp-1  | [3.744ms] [rows:1] UPDATE books SET updated_at=CURRENT_TIMESTAMP, title='Death Note', author='Misa Amane', published_date='2004-11-04 00:00:00', image_url='https://static.wikia.nocookie.net/deathnote/images/9/94/A_Death_Note.jpg', description='Light Yagami''s buried notebook' WHERE id = '38ba23d1-9565-40ed-b781-aacd2f84018d' RETURNING *\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mqesa6hkls7397s45g\",\"id\":\"38ba23d1-9565-40ed-b781-aacd2f84018d\",\"time\":\"2018-01-10T05:00:00+08:00\",\"message\":\"book updated\"}\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mqesa6hkls7397s45g\",\"received_time\":\"2018-01-10T05:00:00+08:00\",\"method\":\"PUT\",\"url\":\"/v1/books/38ba23d1-9565-40ed-b781-aacd2f84018d\",\"header_size\":135,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":200,\"resp_header_size\":47,\"resp_body_size\":252,\"latency\":4.018875,\"time\":\"2018-01-10T05:00:00+08:00\"}\napp-1  |\napp-1  | [3.035ms] [rows:1] DELETE FROM books WHERE id = '38ba23d1-9565-40ed-b781-aacd2f84018d' RETURNING true\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mqfgi6hkls7397s460\",\"received_time\":\"2018-01-10T06:00:00+08:00\",\"method\":\"DELETE\",\"url\":\"/v1/books/38ba23d1-9565-40ed-b781-aacd2f84018d\",\"header_size\":82,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":200,\"resp_header_size\":47,\"resp_body_size\":0,\"latency\":3.265,\"time\":\"2018-01-10T06:00:00+08:00\"}\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mqfgi6hkls7397s460\",\"id\":\"38ba23d1-9565-40ed-b781-aacd2f84018d\",\"time\":\"2018-01-10T06:00:00+08:00\",\"message\":\"book deleted\"}\napp-1  |\napp-1  | [2.573ms] [rows:1] SELECT * FROM books LIMIT 10 OFFSET 0\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mq9gi6hkls7397s440\",\"received_time\":\"2018-01-10T07:00:00+08:00\",\"method\":\"GET\",\"url\":\"/v1/books\",\"header_size\":82,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":200,\"resp_header_size\":47,\"resp_body_size\":298,\"latency\":2.926916,\"time\":\"2018-01-10T07:00:00+08:00\"}\napp-1  |\napp-1  |\napp-1  | [1.661ms] [rows:0] DELETE FROM books WHERE id = '38ba23d1-9565-40ed-b781-aacd2f84018d' RETURNING true\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mrj8ppsdvs73dkfct0\",\"received_time\":\"2018-02-01T01:00:00+08:00\",\"method\":\"DELETE\",\"url\":\"/v1/books/38ba23d1-9565-40ed-b781-aacd2f84018d\",\"header_size\":82,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":404,\"resp_header_size\":47,\"resp_body_size\":0,\"latency\":1.80125,\"time\":\"2018-02-01T01:00:00+08:00\"}\napp-1  | [1.384ms] [rows:0] UPDATE books SET updated_at=CURRENT_TIMESTAMP, title='Death Note', author='Misa Amane', published_date='2004-11-04 00:00:00', image_url='https://static.wikia.nocookie.net/deathnote/images/9/94/A_Death_Note.jpg', description='Light Yagami''s buried notebook' WHERE id = '38ba23d1-9565-40ed-b781-aacd2f84018d' RETURNING *\napp-1  | {\"level\":\"info\",\"request_id\":\"d5mqjmhqvtmc73foh3dg\",\"received_time\":\"2018-02-02T08:00:00:00\",\"method\":\"PUT\",\"url\":\"/v1/books/38ba23d1-9565-40ed-b781-aacd2f84018d\",\"header_size\":135,\"body_size\":0,\"agent\":\"yaak\",\"referer\":\"\",\"proto\":\"HTTP/1.1\",\"remote_ip\":\"192.168.65.1\",\"server_ip\":\"172.19.0.3\",\"status\":404,\"resp_header_size\":47,\"resp_body_size\":0,\"latency\":1.576,\"time\":\"2018-02-02T08:00:00+08:00\"}\n\n// 💯 Real logs collected locally but with few rearrangements to make it easier to read.\n```\n\n## 🗂️ Project Folder Structure\n\n```shell\n├── compose.yml\n├── Dockerfile\n│\n├── openapi-v3.yml\n│\n├── app\n│   ├── book\n│   │   ├── bookrepo      # 💡generated with gorm-cli via the interface in book/repository.go\n│   │   │   └── repository.go\n│   │   ├── form_util.go\n│   │   ├── handler.go\n│   │   └── repository.go\n│   └── router\n│       └── router.go\n├── form    # 💡Form validation middleware rely on this and pkg folder only\n│   └── book.go\n├── model\n│   └── book.go\n│\n├── config\n│   └── config.go\n│\n├── cmd   # 💡Entrypoint for app and migrate executables\n│   ├── app\n│   │   └── main.go\n│   └── migrate\n│       ├── main.go\n│       └── migrations\n│           └── 00001_create_books_table.sql\n│\n└── pkg (middleware, logger, validator, ctxutil, paramsutil, errors)\n```\n\nArgoCD and `kustomization` based cloud native IaC \u0026 GitOps setup.\n\n\u003e 💡 Consider moving to a Hub-and-Spoke architecture for Argo CD, combined with a separate repository strategy.\n\n```shell\n└── k8s\n    │\n    ├── bootstrap\n    │   ├── argocd\n    │   └── argocd-config\n    │       ├── clusters\n    │       ├── projects\n    │       └── applications\n    │\n    ├── platform\n    │   ├── metrics-server\n    │   ├── gateway-api\n    │   ├── istio-ambient\n    │   └── cloudnative-pg\n    │\n    ├── components\n    │   └── myapp-db\n    └── services\n        ├── base\n        │   └── myapp\n        └── overlays\n            ├── dev\n            ├── prod\n            └── stage\n```\n","funding_links":["https://github.com/sponsors/dumindu","https://www.buymeacoffee.com/dumindu"],"categories":["Repositories"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearning-cloud-native-go%2Fmyapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flearning-cloud-native-go%2Fmyapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearning-cloud-native-go%2Fmyapp/lists"}