{"id":15099113,"url":"https://github.com/zett-8/go-clean-echo","last_synced_at":"2025-06-10T18:35:11.598Z","repository":{"id":50486648,"uuid":"518454724","full_name":"zett-8/go-clean-echo","owner":"zett-8","description":"Go Echo Simple API with Clean architecture","archived":false,"fork":false,"pushed_at":"2023-03-10T19:22:31.000Z","size":221,"stargazers_count":74,"open_issues_count":0,"forks_count":13,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-10T10:26:24.043Z","etag":null,"topics":["auth0","auth0-jwt","clean-architecture","docker","echo","go","golang","jwt","jwt-authentication","postgres","sql-migrate","swagger","testing"],"latest_commit_sha":null,"homepage":"","language":"Go","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/zett-8.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":"2022-07-27T12:44:23.000Z","updated_at":"2025-03-23T15:52:00.000Z","dependencies_parsed_at":"2024-06-19T06:13:30.384Z","dependency_job_id":"f2e46303-7a1e-453d-906a-bbd30d32efb7","html_url":"https://github.com/zett-8/go-clean-echo","commit_stats":{"total_commits":52,"total_committers":1,"mean_commits":52.0,"dds":0.0,"last_synced_commit":"35bc9993c171edbd3c14704449c4ee5194198942"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zett-8%2Fgo-clean-echo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zett-8%2Fgo-clean-echo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zett-8%2Fgo-clean-echo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zett-8%2Fgo-clean-echo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zett-8","download_url":"https://codeload.github.com/zett-8/go-clean-echo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248974646,"owners_count":21192186,"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":["auth0","auth0-jwt","clean-architecture","docker","echo","go","golang","jwt","jwt-authentication","postgres","sql-migrate","swagger","testing"],"created_at":"2024-09-25T17:05:42.915Z","updated_at":"2025-04-14T22:36:52.482Z","avatar_url":"https://github.com/zett-8.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go Echo Simple API with Clean Architecture\n\n[![Build \u0026 Unit Test](https://github.com/zett-8/go-clean-echo/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/zett-8/go-clean-echo/actions/workflows/test.yml)\n\n## 🤓 About this repo\nThis is a sample of Web API built by Go (Echo) according to *Clean architecture*.  \nBut I have to say that I'm not a backend specialist nor of Go, so Clean architecture here could be wrong or missing essential concepts. (I'm sorry in that case)\n\nThis sample consists of 4 layers, **\"Models\"**, **\"Stores\"**, **\"Services(Logic)\"** and **\"Handlers(Framework)\"**, although naming might differ in other samples.\nEach layer only concerns/handles its inside layer, not the other way around.\nThis makes it super easy to replace each layer. (also good for testing)\n\n![clean architecture](./utils/img.png)\n\n#### What you might find helpful in this repo.\n- [x] [echo](https://github.com/labstack/echo)(Framework) for handling requests\n- [x] Clean architecture\n- [x] zap([uber-go/zap](https://github.com/uber-go/zap)) for logging\n- [x] swagger([swaggo/echo-swagger](https://github.com/swaggo/echo-swagger)) for documentation\n- [x] sql-migrate([rubenv/sql-migrate](https://github.com/rubenv/sql-migrate)) for migration\n- [x] Database transaction in service layer\n- [x] sqlmock ([DATA-DOG/go-sqlmock](https://github.com/DATA-DOG/go-sqlmock)) for testing\n- [x] Multi-stage build for optimized docker image\n- [x] Hot reload([cosmtrek/air](https://github.com/cosmtrek/air)) for efficient development\n- [x] JWT authentication([auth0/go-jwt-middleware](https://github.com/auth0/go-jwt-middleware/)) with [Auth0](https://auth0.com/) for security\n- [x] Automated testing on GitHub actions \n\n\u003cbr /\u003e\n\n## 👟 How to run\nInstall.\n```shell\ngit clone git@github.com:zett-8/go-clean-echo.git\n\ncd go-clean-echo\n```\n\nDownload dependencies.\n```shell\ngo mod download\n```\n\nFill out auth0 config to run with JWT authentication. Or simply disable JWT middleware\n```go\n// configs/auth0.go\nvar Auth0Config = Auth0ConfigType{\n    Domain:             \"****\",\n    ClientID:           \"****\",\n    Audience:           []string{\"****\"},\n    Issuer:             \"****\",\n    SignatureAlgorithm: validator.RS256,\n    CacheDuration:      15 * time.Minute,\n}\n\n// or \n\n// handlers/handlers.go\nfunc SetApi(e *echo.Echo, h *Handlers, m echo.MiddlewareFunc) {\n    g := e.Group(\"/api/v1\")\n    g.Use(m) // \u003c- Comment out this line\n}\n```\n\nRun docker.\n```shell\ndocker-compose up\n```\n\n\u003cbr /\u003e\n\n## 🌱 Tips \n\n### Use multi-stage build\nUsing multistage build reduces the size of the docker image.\n\n```dockerfile\nARG PORT=8888\n\n# Base image for local development. Use 'air' for hot reload.\nFROM golang:1.18-alpine as base\n\nARG PORT\nENV PORT=$PORT\nENV GO_ENV=development\n\nWORKDIR /go/app/base\n\nCOPY go.mod .\nCOPY go.sum .\n\nRUN apk add build-base\nRUN go mod download\nRUN go install github.com/cosmtrek/air@latest\n\nCOPY . .\n\n\n# The image for build. Set CGO_ENABLE=0 not to build unnecessary binary.\nFROM golang:1.18-alpine as builder\n\nARG PORT\nENV PORT=$PORT\n\nWORKDIR /go/app/builder\n\nCOPY --from=base /go/app/base /go/app/builder\n\nRUN CGO_ENABLED=0 go build -o main -ldflags \"-s -w\"\n\n\n# The final image to run \nFROM gcr.io/distroless/static-debian11 as production\n\nARG PORT\nENV PORT=$PORT\n\nWORKDIR /go/app/src\n\nCOPY --from=builder /go/app/builder/main /go/app/src/main\n\nEXPOSE $PORT\n\nCMD [\"/go/app/src/main\"]\n```\n\n### Use \"Air\" for hot reload\nWe used to have other reloaders such as [realize](https://github.com/oxequa/realize).\nBut it seems no longer be developed or maintained, so I recommend to use \"air\"\n\n\u003e [cosmtrek/air](https://github.com/cosmtrek/air).\n\n### Mock a unit for testing\nHere is one example to mock \"service\" for \"handler\" test.\n\nLet's say this is the service to mock.\n```go\n// services/services.go\ntype Services struct {\n    AuthorService\n    BookService\n}\n\nfunc New(s *stores.Stores) *Services {\n    return \u0026Services{\n        AuthorService: \u0026authorService{s.AuthorStore},\n        BookService:   \u0026bookService{s.BookStore},\n    }\n}\n\n// services/author.go\ntype (\n    AuthorService interface {\n        GetAuthors() ([]models.Author, error)\n        DeleteAuthor(id int) error\n    }\n\n    authorService struct {\n        store stores.AuthorStore\n    }\n)\n\nfunc (s *AuthorServiceContext) GetAuthors() ([]models.Author, error) {\n\tr, err := s.store.Get()\n\treturn r, err\n}\n\nfunc (s *AuthorServiceContext) DeleteAuthor(id int) error {\n\terr := s.store.DeleteById(id)\n\treturn err\n}\n```\n\nAnd in /handlers/author_test.go. Declare Mock struct.\n```go\n// handlers/author_test.go\ntype MockAuthorService struct {\n\tservices.AuthorService\n\tMockGetAuthors       func() ([]models.Author, error)\n\tMockDeleteAuthorById func(id int) error\n}\n\nfunc (m *MockAuthorService) GetAuthors() ([]models.Author, error) {\n\treturn m.MockGetAuthors()\n}\n\nfunc (m *MockAuthorService) DeleteAuthor(id int) error {\n\treturn m.MockDeleteAuthorById(id)\n}\n```\n\nThen use it as mockService.\n```go\n// handlers/author_test.go\nfunc TestGetAuthorsSuccessCase(t *testing.T) {\n    s := \u0026MockAuthorService{\n        MockGetAuthors: func () ([]models.Author, error) {\n            var r []models.Author\n            return r, nil\n        },\n    }\n    \n    mockService := \u0026services.Services{AuthorService: s}\n    \n    e := Echo()\n    h := New(mockService)\n}\n```\n\n\u003cbr /\u003e\n\n## 📃 API Document (Swagger)\n```text\nhttp://localhost:8888/swagger/index.html\n```\n\n\u003cbr /\u003e\n\n## ✅ Testing\n```shell\nmake test\n```\n\n\u003cbr /\u003e\n\n## 💜 References\n[bxcodec/go-clean-arch](https://github.com/bxcodec/go-clean-arch)  \n[onakrainikoff/echo-rest-api](https://github.com/onakrainikoff/echo-rest-api)  \n[satishbabariya/go-echo-auth0-middleware](https://github.com/satishbabariya/go-echo-auth0-middleware)  \n[xesina/golang-echo-realworld-example-app](https://github.com/xesina/golang-echo-realworld-example-app)  \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzett-8%2Fgo-clean-echo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzett-8%2Fgo-clean-echo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzett-8%2Fgo-clean-echo/lists"}