├── .gitignore ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── api ├── grpc │ └── grpc.go └── rest │ ├── handler │ └── handler.go │ ├── middleware │ └── middleware.go │ └── router │ └── router.go ├── cmd ├── cli │ └── main.go └── go-api-microservice │ └── main.go ├── docker └── Dockerfile ├── go.mod ├── go.sum ├── internal ├── bootstrap │ ├── env.go │ └── init.go ├── config │ ├── config.go │ ├── env │ │ ├── dev │ │ │ └── config.yaml │ │ ├── prod │ │ │ └── config.yaml │ │ └── test │ │ │ └── config.yaml │ └── models.go ├── dal │ ├── cache │ │ ├── appcache │ │ │ ├── appcache.go │ │ │ └── methods.go │ │ ├── factory.go │ │ ├── interface.go │ │ └── rediscache │ │ │ ├── methods.go │ │ │ └── rediscache.go │ ├── dal.go │ ├── db │ │ ├── factory.go │ │ ├── interface.go │ │ ├── mongo │ │ │ ├── methods.go │ │ │ └── mongo.go │ │ └── mysql │ │ │ ├── methods.go │ │ │ └── mysql.go │ └── entities │ │ ├── address │ │ ├── address.go │ │ └── models.go │ │ └── user │ │ ├── models.go │ │ └── user.go ├── dependencies │ └── deps.go ├── models │ └── index.go └── server │ ├── grpc │ └── grpc.go │ ├── http │ └── http.go │ └── server.go ├── pkg ├── cache │ ├── appcache │ │ ├── appcache.go │ │ └── config.go │ └── redis │ │ ├── config.go │ │ └── redis.go ├── db │ ├── mongo │ │ ├── config.go │ │ └── mongo.go │ └── mysql │ │ ├── config.go │ │ └── mysql.go ├── logger │ ├── config.go │ └── logger.go └── utils │ ├── constants.go │ └── utils.go ├── scripts └── bump_version.sh └── test └── server_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries and build outputs 2 | .build 3 | 4 | # Go 5 | vendor/ 6 | 7 | # IDEs and editors 8 | .vscode/ 9 | .idea/ -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global owner 2 | * @lokesh-go -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to go-api-microservice 2 | 3 | Thanks for taking the time to contribute! 4 | 5 | ## 👣 How to Contribute 6 | 7 | 1. **Fork** the repository. 8 | 2. **Clone** your fork: 9 | `git clone https://github.com/lokesh-go/go-api-microservice.git` 10 | 3. Create a new branch from master: 11 | `git checkout -b feature/my-feature` 12 | 4. Make your changes. 13 | 5. Commit your changes: 14 | `git commit -m "feat: add new feature"` 15 | 6. Push to your branch: 16 | `git push origin feature/my-feature` 17 | 7. Open a **Pull Request** targeting the master branch on the main repository. 18 | 19 | --- 20 | 21 | ## ✅ Before Submitting 22 | 23 | ### Please ensure you complete the following checklist before submitting a PR: 24 | - make format 25 | - make test 26 | - make build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Lokesh Chandra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Project variables 2 | VERSION := $(shell cat VERSION) 3 | GOOS ?= $(shell go env GOOS) 4 | GOARCH ?= $(shell go env GOARCH) 5 | APP_NAME := go-api-microservice 6 | ENTRYPOINT := cmd/go-api-microservice 7 | BUILD_DIR := .build 8 | BINARY_NAME := $(APP_NAME)-$(VERSION) 9 | DOCKER_IMAGE := $(APP_NAME):$(VERSION) 10 | DOCKERFILE := docker/Dockerfile 11 | PORT := 80 12 | INTERNAL_PORT := 8080 13 | ENV ?= dev 14 | DEFAULT_BRANCH ?= master 15 | 16 | .PHONY: build run build-docker run-docker clean test format release help 17 | 18 | build: 19 | @echo "🛠️ Building $(APP_NAME) for $(GOOS)/$(GOARCH) [ENV=$(ENV)]" 20 | @mkdir -p $(BUILD_DIR) 21 | GOOS=$(GOOS) GOARCH=$(GOARCH) \ 22 | go build -ldflags "-s -w" \ 23 | -o $(BUILD_DIR)/$(BINARY_NAME) ./$(ENTRYPOINT) 24 | 25 | run: 26 | @echo "🚀 Running $(APP_NAME) with ENV=$(ENV)" 27 | ENV="$(ENV)" go run $(ENTRYPOINT)/main.go 28 | 29 | build-docker: 30 | @echo "🐳 Building Docker image: $(DOCKER_IMAGE)" 31 | docker build --build-arg ENV=$(ENV) -t $(DOCKER_IMAGE) -f $(DOCKERFILE) . 32 | 33 | run-docker: 34 | @echo "🐳 Running Docker container: $(DOCKER_IMAGE)" 35 | docker run --rm \ 36 | -p $(PORT):$(PORT) \ 37 | -p $(INTERNAL_PORT):$(INTERNAL_PORT) \ 38 | --env ENV=$(ENV) \ 39 | $(DOCKER_IMAGE) 40 | 41 | clean: 42 | @echo "🧹 Cleaning build artifacts" 43 | @go clean -cache -modcache 44 | @rm -rf $(BUILD_DIR) 45 | @echo "✅ Clean complete." 46 | 47 | test: 48 | @echo "🧪 Running test cases" 49 | go test ./test/... 50 | 51 | format: 52 | @echo "🧼 Formatting source code" 53 | go fmt ./... 54 | 55 | release: 56 | @echo "🔁 Switching to $(DEFAULT_BRANCH) branch..." 57 | @git checkout $(DEFAULT_BRANCH) 58 | @git pull origin $(DEFAULT_BRANCH) 59 | 60 | @echo "🚀 Bumping version using: $(bump)" 61 | @./scripts/bump_version.sh $(bump) 62 | 63 | @git add VERSION 64 | @git commit -m "chore: bump version to $$(cat VERSION)" 65 | @git tag $$(cat VERSION) 66 | 67 | @echo "📤 Pushing release branch and tag..." 68 | @git push origin $(DEFAULT_BRANCH) 69 | @git push origin $$(cat VERSION) 70 | 71 | @echo "✅ Release pushed successfully!" 72 | 73 | help: 74 | @echo "📘 Available Makefile commands:" 75 | @echo " build - Build Go binary into .build/ [default ENV=dev]" 76 | @echo " run - Run app using go run" 77 | @echo " build-docker - Build Docker image from Dockerfile" 78 | @echo " run-docker - Run Docker container locally" 79 | @echo " clean - Clean Go caches and .build directory" 80 | @echo " test - Run test suite" 81 | @echo " format - Format code using go fmt" 82 | @echo " release - Usage: make release bump=patch|minor|major" 83 | @echo " help - Show this help message" 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-api-microservice 2 | 3 | A Go microservice boilerplate that supports both REST and gRPC APIs. No need to worry about project structure—just clone the repo and start writing your business logic. 4 | 5 | ## Core Packages 6 | 7 | ### HTTP Framework 8 | 9 | - **[github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) v1.10.0** 10 | - High-performance HTTP web framework 11 | - Provides middleware support and routing 12 | - Built-in input validation and error handling 13 | 14 | ### Application Cache 15 | 16 | - **[github.com/patrickmn/go-cache](https://github.com/patrickmn/go-cache) v2.1.0** 17 | - In-memory key-value store 18 | - Supports TTL and automatic cleanup 19 | - Thread-safe operations 20 | 21 | ### Configuration Management 22 | 23 | - **[gopkg.in/yaml.v3](https://pkg.go.dev/gopkg.in/yaml.v3) v3.0.1** 24 | - YAML configuration parser 25 | - Supports environment variable interpolation 26 | - Type-safe configuration loading 27 | 28 | ## Project Structure 29 | 30 | ```bash 31 | . 32 | ├── api/ # API layer: REST and gRPC endpoints 33 | │ ├── grpc/ # gRPC service implementations 34 | │ │ └── grpc.go # gRPC service entry point 35 | │ │ 36 | │ └── rest/ # REST API components 37 | │ ├── handler/ # HTTP request handlers 38 | │ ├── middleware/ # Request processing middleware 39 | │ └── router/ # URL routing definitions 40 | │ 41 | ├── cmd/ # Application entry points 42 | │ ├── go-api-microservice/ # Main API server executable 43 | │ │ └── main.go # Server initialization 44 | │ │ 45 | │ └── cli/ # Command-line tools 46 | │ └── main.go # CLI entry point 47 | │ 48 | ├── docker/ # Container configuration 49 | │ └── Dockerfile # Multi-stage build definition 50 | │ 51 | ├── internal/ # Private application code 52 | │ ├── bootstrap/ # App initialization & setup 53 | │ │ ├── env.go # Environment configuration 54 | │ │ └── init.go # Bootstrap sequence 55 | │ │ 56 | │ ├── config/ # Configuration management 57 | │ │ ├── config.go # Config loading logic 58 | │ │ └── models.go # Config structure definitions 59 | │ │ 60 | │ ├── dal/ # Data Access Layer 61 | │ │ ├── cache/ # Caching implementations 62 | │ │ │ ├── appcache/ # Local memory cache 63 | │ │ │ └── rediscache/ # Redis cache client 64 | │ │ │ 65 | │ │ ├── db/ # Database implementations 66 | │ │ │ ├── mongo/ # MongoDB client & operations 67 | │ │ │ └── mysql/ # MySQL client & operations 68 | │ │ │ 69 | │ │ └── entities/ # Domain model definitions 70 | │ │ ├── address/ # Address related models 71 | │ │ └── user/ # User related models 72 | │ │ 73 | │ ├── dependencies/ # Dependency injection container 74 | │ │ 75 | │ ├── models/ # Shared domain models 76 | │ │ 77 | │ │── modules/ # Business logic for all entities 78 | │ │ 79 | │ └── server/ # Server implementations 80 | │ ├── grpc/ # gRPC server setup 81 | │ ├── http/ # HTTP server setup 82 | │ └── server.go # Server interface definitions 83 | │ 84 | ├── pkg/ # Shared libraries and utilities 85 | │ ├── cache/ # Generic caching interfaces 86 | │ ├── db/ # Database utilities and interfaces 87 | │ ├── logger/ # Logging utilities and abstractions 88 | │ └── utils/ # Common helper and utility functions 89 | 90 | ├── scripts/ # Build and deployment scripts 91 | │ ├── build.sh # Build automation 92 | │ └── run.sh # Runtime scripts 93 | │ 94 | ├── test/ # Test suites and test data 95 | | 96 | |-- go.mod 97 | | 98 | |-- go.sum 99 | | 100 | |__ Makefile 101 | 102 | ``` 103 | 104 | ## Getting Started 105 | 106 | ### Prerequisites 107 | 108 | - Go 1.23 or higher 109 | 110 | ### Project Setup 111 | 112 | 1. Clone the repository: 113 | 114 | ```bash 115 | git clone https://github.com/lokesh-go/go-api-microservice.git 116 | cd go-api-microservice 117 | ``` 118 | 119 | 2. Install dependencies: 120 | 121 | ```bash 122 | go mod download 123 | ``` 124 | 125 | 3. Set up environment variables: 126 | 127 | ```bash 128 | Project supports three enviroments. 129 | 130 | # dev ( Default ) 131 | export ENV=dev 132 | 133 | # test 134 | export ENV=test 135 | 136 | # prod 137 | export ENV=prod 138 | ``` 139 | 140 | 4. Run the application: 141 | 142 | ```bash 143 | make run 144 | ``` 145 | 146 | 5. Run the application using Docker: 147 | 148 | ```bash 149 | # Build docker image 150 | make build-docker 151 | 152 | # Run docker image 153 | make run-docker 154 | ``` 155 | 156 | 6. Ping your application: 157 | 158 | ```bash 159 | # Health check for your application 160 | http://localhost/ping 161 | ``` 162 | 163 | ## License 164 | 165 | This project is licensed under the [MIT License](./LICENSE) © 2025 Lokesh Chandra. -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v1.1.7 2 | -------------------------------------------------------------------------------- /api/grpc/grpc.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | // Here we can handle & define gRPC routes 4 | -------------------------------------------------------------------------------- /api/rest/handler/handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/lokesh-go/go-api-microservice/api/rest/middleware" 8 | "github.com/lokesh-go/go-api-microservice/internal/dependencies" 9 | ) 10 | 11 | // Handler 12 | type Handler struct { 13 | deps *dependencies.Deps 14 | } 15 | 16 | // New handler 17 | func New(d *dependencies.Deps) *Handler { 18 | return &Handler{ 19 | deps: d, 20 | } 21 | } 22 | 23 | // Ping ... 24 | func (h *Handler) Ping(c *gin.Context) { 25 | // Get meta data 26 | var metadata middleware.RequestMetaData 27 | if ctxMetadata, exists := c.Get(middleware.CtxKeyReqMeta); exists { 28 | metadata = ctxMetadata.(middleware.RequestMetaData) 29 | } 30 | 31 | // Prepare response 32 | appConfig := h.deps.Config.Get().App 33 | res := map[string]interface{}{ 34 | "appName": appConfig.Name, 35 | "appVersion": appConfig.Version, 36 | "description": appConfig.Description, 37 | "auther": appConfig.Author, 38 | "clientIP": metadata.ClientIP, 39 | } 40 | 41 | // Returns 42 | c.JSON(http.StatusOK, res) 43 | } 44 | -------------------------------------------------------------------------------- /api/rest/middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // Keys to store in Gin context 11 | const ( 12 | CtxKeyReqMeta = "req_metadata" 13 | ) 14 | 15 | // Request meta data 16 | type RequestMetaData struct { 17 | TimeStamp time.Time 18 | ClientIP string 19 | UserAgent string 20 | Method string 21 | Path string 22 | ContentLength int64 23 | } 24 | 25 | // Capture the request meta data 26 | func CaptureRequestMetaData() gin.HandlerFunc { 27 | return func(c *gin.Context) { 28 | meta := RequestMetaData{ 29 | TimeStamp: time.Now().UTC(), 30 | ClientIP: c.ClientIP(), 31 | UserAgent: c.Request.UserAgent(), 32 | Method: c.Request.Method, 33 | Path: c.Request.URL.Path, 34 | ContentLength: c.Request.ContentLength, 35 | } 36 | 37 | // Store meta dat into context 38 | c.Set(CtxKeyReqMeta, meta) 39 | 40 | // Continue processing 41 | c.Next() 42 | } 43 | } 44 | 45 | // Custom logger 46 | func CustomLogger() gin.HandlerFunc { 47 | return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { 48 | return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", 49 | param.ClientIP, 50 | param.TimeStamp.Format(time.RFC1123), 51 | param.Method, 52 | param.Path, 53 | param.Request.Proto, 54 | param.StatusCode, 55 | param.Latency, 56 | param.Request.UserAgent(), 57 | param.ErrorMessage, 58 | ) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /api/rest/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/lokesh-go/go-api-microservice/api/rest/handler" 6 | "github.com/lokesh-go/go-api-microservice/api/rest/middleware" 7 | "github.com/lokesh-go/go-api-microservice/internal/dependencies" 8 | ) 9 | 10 | // Router interface 11 | type Router interface { 12 | RegisterPublicRoutes() *gin.Engine 13 | RegisterInternalRoutes() *gin.Engine 14 | } 15 | 16 | type router struct { 17 | handler *handler.Handler 18 | } 19 | 20 | // New router 21 | func New(d *dependencies.Deps) Router { 22 | return &router{ 23 | handler: handler.New(d), 24 | } 25 | } 26 | 27 | // Register public routes 28 | func (r *router) RegisterPublicRoutes() *gin.Engine { 29 | // Initialise gin router 30 | router := gin.New() 31 | 32 | // Use middlewares 33 | router.Use(middleware.CustomLogger()) 34 | router.Use(gin.Recovery()) 35 | router.Use(middleware.CaptureRequestMetaData()) 36 | 37 | // Define routes 38 | router.GET("/ping", r.handler.Ping) 39 | 40 | /* 41 | public := router.Group("/api/v1") 42 | { 43 | user := public.Group("/users") 44 | { 45 | user.GET("/:userId", r.handler.GetUser) 46 | } 47 | } 48 | */ 49 | 50 | // Returns 51 | return router 52 | } 53 | 54 | // Register internal routes 55 | func (r *router) RegisterInternalRoutes() *gin.Engine { 56 | // Initialise gin router 57 | router := gin.New() 58 | 59 | // Use middlewares 60 | router.Use(middleware.CustomLogger()) 61 | router.Use(gin.Recovery()) 62 | router.Use(middleware.CaptureRequestMetaData()) 63 | 64 | // Define routes 65 | router.GET("/ping", r.handler.Ping) 66 | 67 | /* 68 | public := router.Group("/internal/api/v1") 69 | { 70 | } 71 | */ 72 | 73 | // Returns 74 | return router 75 | } 76 | -------------------------------------------------------------------------------- /cmd/cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // CLI start point 4 | func main() { 5 | // Initialize the CLI 6 | } 7 | -------------------------------------------------------------------------------- /cmd/go-api-microservice/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/lokesh-go/go-api-microservice/internal/bootstrap" 7 | ) 8 | 9 | // API Server start point 10 | func main() { 11 | // Initialize the server 12 | err := bootstrap.Initialize() 13 | if err != nil { 14 | log.Fatalf("api server initialization failed: %v", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # -------- Stage 1: Build -------- 2 | FROM golang:1.23 as builder 3 | 4 | # Set environment variables to build a static binary 5 | ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm64 6 | 7 | # Set the working directory inside the container 8 | WORKDIR /app 9 | 10 | # Cache dependencies 11 | COPY go.mod go.sum ./ 12 | RUN go mod download 13 | 14 | # Copy the entire project 15 | COPY . . 16 | 17 | # Build the Go binary 18 | RUN go build -o /app/go-api-microservice ./cmd/go-api-microservice/main.go 19 | 20 | 21 | # -------- Stage 2: Run -------- 22 | FROM gcr.io/distroless/static:nonroot 23 | 24 | # Accept environment as a build argument 25 | ARG ENV=dev 26 | ENV ENV=${ENV} 27 | 28 | # Set working directory to /app (same as project root) 29 | WORKDIR /app 30 | 31 | # Copy only the binary and the required config files 32 | COPY --from=builder /app/go-api-microservice . 33 | COPY --from=builder /app/internal/config/env/${ENV} /app/internal/config/env/${ENV} 34 | 35 | # Expose the app port (change if needed) 36 | EXPOSE 80 8080 37 | 38 | # Use a non-root user (automatically set in distroless) 39 | ENTRYPOINT ["/app/go-api-microservice"] -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lokesh-go/go-api-microservice 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.9 6 | 7 | require ( 8 | github.com/gin-gonic/gin v1.10.0 9 | github.com/patrickmn/go-cache v2.1.0+incompatible 10 | github.com/stretchr/testify v1.9.0 11 | golang.org/x/sync v0.14.0 12 | gopkg.in/yaml.v3 v3.0.1 13 | ) 14 | 15 | require ( 16 | github.com/bytedance/sonic v1.11.6 // indirect 17 | github.com/bytedance/sonic/loader v0.1.1 // indirect 18 | github.com/cloudwego/base64x v0.1.4 // indirect 19 | github.com/cloudwego/iasm v0.2.0 // indirect 20 | github.com/davecgh/go-spew v1.1.1 // indirect 21 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 22 | github.com/gin-contrib/sse v0.1.0 // indirect 23 | github.com/go-playground/locales v0.14.1 // indirect 24 | github.com/go-playground/universal-translator v0.18.1 // indirect 25 | github.com/go-playground/validator/v10 v10.20.0 // indirect 26 | github.com/goccy/go-json v0.10.2 // indirect 27 | github.com/json-iterator/go v1.1.12 // indirect 28 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 29 | github.com/leodido/go-urn v1.4.0 // indirect 30 | github.com/mattn/go-isatty v0.0.20 // indirect 31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 32 | github.com/modern-go/reflect2 v1.0.2 // indirect 33 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 34 | github.com/pmezard/go-difflib v1.0.0 // indirect 35 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 36 | github.com/ugorji/go/codec v1.2.12 // indirect 37 | golang.org/x/arch v0.8.0 // indirect 38 | golang.org/x/crypto v0.23.0 // indirect 39 | golang.org/x/net v0.25.0 // indirect 40 | golang.org/x/sys v0.20.0 // indirect 41 | golang.org/x/text v0.15.0 // indirect 42 | google.golang.org/protobuf v1.34.1 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= 2 | github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= 3 | github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= 4 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 5 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 6 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 7 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 8 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 13 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 14 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 15 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 16 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 17 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 18 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 19 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 20 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 21 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 22 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 23 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 24 | github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= 25 | github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 26 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 27 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 28 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 29 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 30 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 31 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 32 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 33 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 34 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 35 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 36 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 37 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 38 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 39 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 40 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 41 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 43 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 44 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 45 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 46 | github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= 47 | github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 48 | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= 49 | github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 50 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 51 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 52 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 53 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 54 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 55 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 56 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 57 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 58 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 59 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 60 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 61 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 62 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 63 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 64 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 65 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 66 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 67 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 68 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 69 | golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= 70 | golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 71 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 72 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 73 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 74 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 75 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= 76 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 77 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 78 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 79 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 80 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 81 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 82 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 83 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 84 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 85 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 86 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 87 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 88 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 89 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 90 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 91 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 92 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 93 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 94 | -------------------------------------------------------------------------------- /internal/bootstrap/env.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import "github.com/lokesh-go/go-api-microservice/pkg/utils" 4 | 5 | func getEnv() (env string) { 6 | env = utils.GetEnv(utils.EnvKey) 7 | if utils.IsEmpty(env) { 8 | env = utils.EnvDefault 9 | } 10 | return env 11 | } 12 | -------------------------------------------------------------------------------- /internal/bootstrap/init.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | "github.com/lokesh-go/go-api-microservice/internal/dal" 6 | "github.com/lokesh-go/go-api-microservice/internal/dependencies" 7 | "github.com/lokesh-go/go-api-microservice/internal/server" 8 | "github.com/lokesh-go/go-api-microservice/pkg/logger" 9 | ) 10 | 11 | // Initialize application dependencies 12 | func Initialize() (err error) { 13 | // Get environment 14 | env := getEnv() 15 | 16 | // Initialize config 17 | conf, err := config.Initialize(env) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | // Initialize logger 23 | logger := logger.New(getLoggerConfig(env, conf)) 24 | logger.Log.Info("logger initialised ...") 25 | 26 | // Initialize data access layer 27 | dal, err := dal.Initialize(conf) 28 | if err != nil { 29 | return err 30 | } 31 | logger.Log.Info("data access layer initialised ...") 32 | 33 | // Starts server 34 | err = server.Start(&dependencies.Deps{ 35 | Config: conf, 36 | Logger: logger, 37 | DAL: dal, 38 | }) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | // Returns 44 | return nil 45 | } 46 | 47 | func getLoggerConfig(env string, config config.Methods) *logger.Config { 48 | // Returns 49 | return &logger.Config{ 50 | Env: env, 51 | AppName: config.Get().App.Name, 52 | AppVersion: config.Get().App.Version, 53 | DebugEnabled: config.Get().Logger.Debug, 54 | CallerSkipNo: config.Get().Logger.CallerSkipNo, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | 7 | "github.com/lokesh-go/go-api-microservice/pkg/utils" 8 | ) 9 | 10 | // Methods for config 11 | type Methods interface { 12 | Get() (c *config) 13 | } 14 | 15 | // Initialize the config 16 | func Initialize(env string) (Methods, error) { 17 | // Read the config file 18 | data, err := utils.ReadFile(resolveConfigPath(env)) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | // Bind the config 24 | var cfg *config 25 | err = utils.YAMLUnmarshal(data, &cfg) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | // Returns the config 31 | return cfg, nil 32 | } 33 | 34 | // Get the config 35 | func (c *config) Get() (conf *config) { 36 | return c 37 | } 38 | 39 | func resolveConfigPath(env string) (path string) { 40 | _, currentFile, _, _ := runtime.Caller(0) 41 | basePath := filepath.Join(filepath.Dir(currentFile), "..", "config", "env", env) 42 | return filepath.Join(basePath, "config.yaml") 43 | } 44 | -------------------------------------------------------------------------------- /internal/config/env/dev/config.yaml: -------------------------------------------------------------------------------- 1 | app: 2 | name: "go-api-microservice" 3 | version: "1.0.0" 4 | description: "A simple REST and gRPC API server written in Go" 5 | author: "Lokesh Chandra" 6 | url: "https://github.com/lokesh-go" 7 | server: 8 | http: 9 | publicAddr: ":80" 10 | internalAddr: ":8080" 11 | readTimeoutInSeconds: 5 12 | writeTimeoutInSeconds: 10 13 | shutdownTimeoutInSeconds: 5 14 | logger: 15 | debug: true 16 | callerSkipNo: 3 -------------------------------------------------------------------------------- /internal/config/env/prod/config.yaml: -------------------------------------------------------------------------------- 1 | app: 2 | name: "go-api-microservice" 3 | version: "1.0.0" 4 | description: "A simple REST and gRPC API server written in Go" 5 | author: "Lokesh Chandra" 6 | url: "https://github.com/lokesh-go" 7 | server: 8 | http: 9 | publicAddr: ":80" 10 | internalAddr: ":8080" 11 | readTimeoutInSeconds: 5 12 | writeTimeoutInSeconds: 10 13 | shutdownTimeoutInSeconds: 5 14 | logger: 15 | debug: true 16 | callerSkipNo: 3 -------------------------------------------------------------------------------- /internal/config/env/test/config.yaml: -------------------------------------------------------------------------------- 1 | app: 2 | name: "go-api-microservice" 3 | version: "1.0.0" 4 | description: "A simple REST and gRPC API server written in Go" 5 | author: "Lokesh Chandra" 6 | url: "https://github.com/lokesh-go" 7 | server: 8 | http: 9 | publicAddr: ":80" 10 | internalAddr: ":8080" 11 | readTimeoutInSeconds: 5 12 | writeTimeoutInSeconds: 10 13 | shutdownTimeoutInSeconds: 5 14 | logger: 15 | debug: true 16 | callerSkipNo: 3 -------------------------------------------------------------------------------- /internal/config/models.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type config struct { 4 | App appConfig `yaml:"app"` 5 | Server serverConfig `yaml:"server` 6 | Logger loggerConfig `yaml:"logger"` 7 | DAL dalConfig `yaml:"dal"` 8 | } 9 | 10 | type appConfig struct { 11 | Name string `yaml:"name"` 12 | Version string `yaml:"version"` 13 | Description string `yaml:"description"` 14 | Author string `yaml:"author"` 15 | URL string `yaml:"url"` 16 | } 17 | 18 | type serverConfig struct { 19 | HTTP httpServerConfig `yaml:"http"` 20 | GRPC grpcServerConfig `yaml:"grpc"` 21 | } 22 | 23 | type httpServerConfig struct { 24 | PublicAddr string `yaml:"publicAddr"` 25 | InternalAddr string `yaml:"internalAddr"` 26 | ReadTimeoutInSeconds int64 `yaml:"readTimeoutInSeconds"` 27 | WriteTimeoutInSeconds int64 `yaml:"WriteTimeoutInSeconds"` 28 | ShutdownTimeoutInSeconds int64 `yaml:"shutdownTimeoutInSeconds"` 29 | } 30 | 31 | type grpcServerConfig struct{} 32 | 33 | type loggerConfig struct { 34 | Debug bool `yaml:"debug"` 35 | CallerSkipNo int `yaml:"callerSkipNo"` 36 | } 37 | 38 | type dalConfig struct { 39 | Cache cacheConfig `yaml:"cache"` 40 | DB dbConfig `yaml:"db"` 41 | } 42 | 43 | type cacheConfig struct { 44 | AppCache appCacheConfig `yaml:"appCache"` 45 | Redis redisCacheConfig `yaml:"redis"` 46 | } 47 | 48 | type appCacheConfig struct { 49 | Enabled bool `yaml:"enabled"` 50 | DefaultExpirationInSeconds int64 `yaml:"defaultExpirationInSeconds"` 51 | CleanupIntervalInMinutes int64 `yaml:"cleanupIntervalInMinutes"` 52 | } 53 | 54 | type redisCacheConfig struct { 55 | Enabled bool `yaml:"enabled"` 56 | } 57 | 58 | type dbConfig struct{} 59 | -------------------------------------------------------------------------------- /internal/dal/cache/appcache/appcache.go: -------------------------------------------------------------------------------- 1 | package appcache 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | appCachePkg "github.com/lokesh-go/go-api-microservice/pkg/cache/appcache" 6 | ) 7 | 8 | // AppCache conn 9 | type appCache struct { 10 | conn *appCachePkg.AppCache 11 | } 12 | 13 | // New app cache 14 | func New(config config.Methods) *appCache { 15 | // Forms appCache config 16 | ac := &appCachePkg.Config{ 17 | DefaultExpirationInSeconds: config.Get().DAL.Cache.AppCache.DefaultExpirationInSeconds, 18 | CleanupIntervalInMinutes: config.Get().DAL.Cache.AppCache.CleanupIntervalInMinutes, 19 | } 20 | 21 | // Get the appCache connection 22 | return &appCache{ 23 | conn: appCachePkg.New(ac), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /internal/dal/cache/appcache/methods.go: -------------------------------------------------------------------------------- 1 | package appcache 2 | 3 | // Get data from appCache 4 | func (a *appCache) Get(key string, val interface{}) (err error) { 5 | // Returns 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /internal/dal/cache/factory.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | appCacheDAL "github.com/lokesh-go/go-api-microservice/internal/dal/cache/appcache" 6 | redisCacheDAL "github.com/lokesh-go/go-api-microservice/internal/dal/cache/rediscache" 7 | ) 8 | 9 | type cacheType string 10 | 11 | const ( 12 | appCache cacheType = "appCache" 13 | redisCache cacheType = "redisCache" 14 | cacheEnabled cacheType = appCache 15 | ) 16 | 17 | // New Cache 18 | func New(config config.Methods) (Cache, error) { 19 | // Switch to the cache 20 | switch cacheEnabled { 21 | case redisCache: 22 | { 23 | // Create redis connection 24 | return redisCacheDAL.New(config) 25 | } 26 | default: 27 | { 28 | // Create appCache connection 29 | return appCacheDAL.New(config), nil 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /internal/dal/cache/interface.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | // Cache ... 4 | type Cache interface { 5 | Get(key string, val interface{}) (err error) 6 | } 7 | -------------------------------------------------------------------------------- /internal/dal/cache/rediscache/methods.go: -------------------------------------------------------------------------------- 1 | package rediscache 2 | 3 | // Get data from redis cache 4 | func (r *redisCache) Get(key string, val interface{}) (err error) { 5 | // Returns 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /internal/dal/cache/rediscache/rediscache.go: -------------------------------------------------------------------------------- 1 | package rediscache 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | redisPkg "github.com/lokesh-go/go-api-microservice/pkg/cache/redis" 6 | ) 7 | 8 | // RedisCache conn 9 | type redisCache struct { 10 | conn *redisPkg.RedisCache 11 | } 12 | 13 | // New redis cache 14 | func New(config config.Methods) (*redisCache, error) { 15 | // Forms redis config 16 | rc := &redisPkg.Config{} 17 | 18 | // Get the redis client 19 | conn, err := redisPkg.New(rc) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | // Returns 25 | return &redisCache{ 26 | conn: conn, 27 | }, nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/dal/dal.go: -------------------------------------------------------------------------------- 1 | package dal 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | "github.com/lokesh-go/go-api-microservice/internal/dal/cache" 6 | "github.com/lokesh-go/go-api-microservice/internal/dal/db" 7 | addressEntity "github.com/lokesh-go/go-api-microservice/internal/dal/entities/address" 8 | userEntity "github.com/lokesh-go/go-api-microservice/internal/dal/entities/user" 9 | ) 10 | 11 | // Data Access Layer 12 | type DAL interface { 13 | User() userDAO 14 | Address() addressDAO 15 | } 16 | 17 | type userDAO interface { 18 | GetUserByID(id string) (data *userEntity.UserDocument, err error) 19 | } 20 | 21 | type addressDAO interface { 22 | GetAddressByUserID(userID string) (data *addressEntity.AddressDocument, err error) 23 | } 24 | 25 | type dal struct { 26 | user *userEntity.UserDAL 27 | address *addressEntity.AddressDAL 28 | } 29 | 30 | // Initialize 31 | func Initialize(config config.Methods) (DAL, error) { 32 | // Connects cache 33 | cache, err := cache.New(config) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | // Connects DB 39 | db, err := db.New(config) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | // Returns 45 | return &dal{ 46 | user: userEntity.New(cache, db), 47 | address: addressEntity.New(cache, db), 48 | }, nil 49 | } 50 | 51 | // User 52 | func (d *dal) User() userDAO { 53 | return d.user 54 | } 55 | 56 | // Address 57 | func (d *dal) Address() addressDAO { 58 | return d.address 59 | } 60 | -------------------------------------------------------------------------------- /internal/dal/db/factory.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | mongoDAL "github.com/lokesh-go/go-api-microservice/internal/dal/db/mongo" 6 | mysqlDAL "github.com/lokesh-go/go-api-microservice/internal/dal/db/mysql" 7 | ) 8 | 9 | type dbType string 10 | 11 | const ( 12 | mongoDB dbType = "mongoDB" 13 | mySQL dbType = "mySQL" 14 | dbEnabled dbType = mongoDB 15 | ) 16 | 17 | // New DB 18 | func New(config config.Methods) (DB, error) { 19 | // Switch to the DB 20 | switch dbEnabled { 21 | case mySQL: 22 | { 23 | return mysqlDAL.New(config) 24 | } 25 | default: 26 | { 27 | return mongoDAL.New(config) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /internal/dal/db/interface.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | // DB 4 | type DB interface { 5 | Find() error 6 | } 7 | -------------------------------------------------------------------------------- /internal/dal/db/mongo/methods.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | func (m *mongoDB) Find() (err error) { 4 | // Returns 5 | return nil 6 | } 7 | -------------------------------------------------------------------------------- /internal/dal/db/mongo/mongo.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | mongoPkg "github.com/lokesh-go/go-api-microservice/pkg/db/mongo" 6 | ) 7 | 8 | // MongoDB connection 9 | type mongoDB struct { 10 | conn *mongoPkg.MongoDB 11 | } 12 | 13 | // New mongoDB connection 14 | func New(config config.Methods) (*mongoDB, error) { 15 | // Forms mongoDB config 16 | mongoDBConfig := &mongoPkg.Config{} 17 | 18 | // Creats mongoDB connection 19 | conn, err := mongoPkg.New(mongoDBConfig) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | // Returns 25 | return &mongoDB{ 26 | conn: conn, 27 | }, nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/dal/db/mysql/methods.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | func (m *mySql) Find() (err error) { 4 | // Returns 5 | return nil 6 | } 7 | -------------------------------------------------------------------------------- /internal/dal/db/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | mysqlPkg "github.com/lokesh-go/go-api-microservice/pkg/db/mysql" 6 | ) 7 | 8 | // MySql connection 9 | type mySql struct { 10 | conn *mysqlPkg.MySql 11 | } 12 | 13 | // New mysql connection 14 | func New(config config.Methods) (*mySql, error) { 15 | // Forms mysql config 16 | mysqlConfig := &mysqlPkg.Config{} 17 | 18 | // Creates mysql connection 19 | conn, err := mysqlPkg.New(mysqlConfig) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | // Returns 25 | return &mySql{ 26 | conn: conn, 27 | }, nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/dal/entities/address/address.go: -------------------------------------------------------------------------------- 1 | package address 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/dal/cache" 5 | "github.com/lokesh-go/go-api-microservice/internal/dal/db" 6 | ) 7 | 8 | // Address data access layer 9 | type AddressDAL struct { 10 | cache cache.Cache 11 | db db.DB 12 | } 13 | 14 | // New creates a new address data access layer 15 | func New(cache cache.Cache, db db.DB) *AddressDAL { 16 | return &AddressDAL{ 17 | cache: cache, 18 | db: db, 19 | } 20 | } 21 | 22 | // GetAddressByUserID retrieves an address by user ID 23 | func (a *AddressDAL) GetAddressByUserID(userID string) (data *AddressDocument, err error) { 24 | return nil, nil 25 | } 26 | -------------------------------------------------------------------------------- /internal/dal/entities/address/models.go: -------------------------------------------------------------------------------- 1 | package address 2 | 3 | // AddressDocument represent the db model for an address 4 | type AddressDocument struct{} 5 | 6 | // addressCacheData represent the cache model for an address 7 | type addressCacheData struct{} 8 | -------------------------------------------------------------------------------- /internal/dal/entities/user/models.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | // UserDocument represents the database model for a user 4 | type UserDocument struct{} 5 | 6 | // userCacheData represents the cache model for a user 7 | type userCacheData struct{} 8 | -------------------------------------------------------------------------------- /internal/dal/entities/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/dal/cache" 5 | "github.com/lokesh-go/go-api-microservice/internal/dal/db" 6 | ) 7 | 8 | // User data access layer 9 | type UserDAL struct { 10 | cache cache.Cache 11 | db db.DB 12 | } 13 | 14 | // New creates a new user data access layer 15 | func New(cache cache.Cache, db db.DB) *UserDAL { 16 | return &UserDAL{ 17 | cache: cache, 18 | db: db, 19 | } 20 | } 21 | 22 | // GetUserByID fetch a user data by ID 23 | func (u *UserDAL) GetUserByID(id string) (data *UserDocument, err error) { 24 | return nil, nil 25 | } 26 | -------------------------------------------------------------------------------- /internal/dependencies/deps.go: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/config" 5 | "github.com/lokesh-go/go-api-microservice/internal/dal" 6 | "github.com/lokesh-go/go-api-microservice/pkg/logger" 7 | ) 8 | 9 | // Dependencies hold all application deps 10 | type Deps struct { 11 | Config config.Methods 12 | Logger *logger.Logger 13 | DAL dal.DAL 14 | } 15 | -------------------------------------------------------------------------------- /internal/models/index.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Here we can defines API request, response model. 4 | -------------------------------------------------------------------------------- /internal/server/grpc/grpc.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | // Start 4 | func Start() (err error) { 5 | // Returns 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /internal/server/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "os/signal" 8 | "syscall" 9 | "time" 10 | 11 | "golang.org/x/sync/errgroup" 12 | 13 | "github.com/lokesh-go/go-api-microservice/api/rest/router" 14 | "github.com/lokesh-go/go-api-microservice/internal/dependencies" 15 | "github.com/lokesh-go/go-api-microservice/pkg/utils" 16 | ) 17 | 18 | type httpServer struct { 19 | dep *dependencies.Deps 20 | } 21 | 22 | // New ... 23 | func New(d *dependencies.Deps) *httpServer { 24 | return &httpServer{ 25 | dep: d, 26 | } 27 | } 28 | 29 | // Start 30 | func (s *httpServer) Start() (err error) { 31 | // Register routes 32 | router := router.New(s.dep) 33 | publicRouter := router.RegisterPublicRoutes() 34 | internalRouter := router.RegisterInternalRoutes() 35 | 36 | // Public server 37 | publicServer := &http.Server{ 38 | Addr: s.dep.Config.Get().Server.HTTP.PublicAddr, 39 | Handler: publicRouter, 40 | ReadTimeout: time.Duration(s.dep.Config.Get().Server.HTTP.ReadTimeoutInSeconds) * time.Second, 41 | WriteTimeout: time.Duration(s.dep.Config.Get().Server.HTTP.WriteTimeoutInSeconds) * time.Second, 42 | } 43 | 44 | // Internal server 45 | internalServer := &http.Server{ 46 | Addr: s.dep.Config.Get().Server.HTTP.InternalAddr, 47 | Handler: internalRouter, 48 | ReadTimeout: time.Duration(s.dep.Config.Get().Server.HTTP.ReadTimeoutInSeconds) * time.Second, 49 | WriteTimeout: time.Duration(s.dep.Config.Get().Server.HTTP.WriteTimeoutInSeconds) * time.Second, 50 | } 51 | 52 | // Global ctx that gets cancelled when the OS sends an interrupt/terminate signal (Ctrl+C) 53 | ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) 54 | defer stop() 55 | 56 | // Create an error group with global context 57 | // If any goroutine in 'g' return an error, the entire gctx is cancelled 58 | // If ctx gets cancelled (SIGINT), gctx is cancelled too. 59 | g, gctx := errgroup.WithContext(ctx) 60 | 61 | // Start public server 62 | g.Go(func() error { 63 | // Log 64 | s.dep.Logger.Log.Info(fmt.Sprintf("public http server listening on %s", s.dep.Config.Get().Server.HTTP.PublicAddr)) 65 | 66 | // Start the server 67 | if err := publicServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { 68 | return err 69 | } 70 | return nil 71 | }) 72 | 73 | // Start private server 74 | g.Go(func() error { 75 | // Log 76 | s.dep.Logger.Log.Info(fmt.Sprintf("internal http server listening on %s", s.dep.Config.Get().Server.HTTP.InternalAddr)) 77 | 78 | // Start the server 79 | if err := internalServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { 80 | return err 81 | } 82 | return nil 83 | }) 84 | 85 | // Wait for the servers to finish 86 | g.Go(func() error { 87 | // Wait for the Signal or error 88 | // Any error or signal will cancel the gctx 89 | <-gctx.Done() 90 | 91 | // Log 92 | s.dep.Logger.Log.Info("shutting down servers ...") 93 | 94 | // Timeout for graceful shutdown 95 | shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Duration(s.dep.Config.Get().Server.HTTP.ShutdownTimeoutInSeconds)*time.Second) 96 | defer cancel() 97 | 98 | // Shutdown both servers 99 | var shutdownErr error 100 | if err := publicServer.Shutdown(shutdownCtx); err != nil { 101 | s.dep.Logger.Log.Error("failed to shutdown public server", utils.ErrorJSONKey, err) 102 | shutdownErr = err 103 | } 104 | if err := internalServer.Shutdown(shutdownCtx); err != nil { 105 | s.dep.Logger.Log.Error("failed to shutdown internal server", utils.ErrorJSONKey, err) 106 | shutdownErr = err 107 | } 108 | 109 | // Returns 110 | return shutdownErr 111 | }) 112 | 113 | // Wait for the error group to finish 114 | // TODO :: Need to move out form here to the server it's block the main thread 115 | // so won't be initialised the grpc server 116 | if err := g.Wait(); err != nil { 117 | return err 118 | } 119 | 120 | // Returns 121 | return nil 122 | } 123 | -------------------------------------------------------------------------------- /internal/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/lokesh-go/go-api-microservice/internal/dependencies" 5 | grpcServer "github.com/lokesh-go/go-api-microservice/internal/server/grpc" 6 | httpServer "github.com/lokesh-go/go-api-microservice/internal/server/http" 7 | ) 8 | 9 | // Start 10 | func Start(d *dependencies.Deps) (err error) { 11 | // Starts http server 12 | err = httpServer.New(d).Start() 13 | if err != nil { 14 | return err 15 | } 16 | 17 | // Starts grpc server 18 | grpcServer.Start() 19 | 20 | // Returns 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /pkg/cache/appcache/appcache.go: -------------------------------------------------------------------------------- 1 | package appcache 2 | 3 | import ( 4 | "time" 5 | 6 | goCache "github.com/patrickmn/go-cache" 7 | ) 8 | 9 | // AppCache ... 10 | type AppCache struct { 11 | Cache *goCache.Cache 12 | } 13 | 14 | // New ... 15 | func New(config *Config) *AppCache { 16 | // Override config 17 | if config != nil && config.DefaultExpirationInSeconds != 0 { 18 | defaultExpirationInSeconds = config.DefaultExpirationInSeconds 19 | } 20 | if config != nil && config.CleanupIntervalInMinutes != 0 { 21 | cleanupIntervalInMinutes = config.CleanupIntervalInMinutes 22 | } 23 | 24 | // Initialise 25 | defaultExpiration := time.Duration(defaultExpirationInSeconds) * time.Second 26 | cleanupInterval := time.Duration(cleanupIntervalInMinutes) * time.Minute 27 | cache := goCache.New(defaultExpiration, cleanupInterval) 28 | 29 | // Returns 30 | return &AppCache{ 31 | Cache: cache, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/cache/appcache/config.go: -------------------------------------------------------------------------------- 1 | package appcache 2 | 3 | var ( 4 | defaultExpirationInSeconds int64 = 300 5 | cleanupIntervalInMinutes int64 = 10 6 | ) 7 | 8 | // Config ... 9 | type Config struct { 10 | DefaultExpirationInSeconds int64 11 | CleanupIntervalInMinutes int64 12 | } 13 | -------------------------------------------------------------------------------- /pkg/cache/redis/config.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | // Config ... 4 | type Config struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/cache/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | // RedisCache ... 4 | type RedisCache struct{} 5 | 6 | // New ... 7 | func New(config *Config) (*RedisCache, error) { 8 | // Returns 9 | return &RedisCache{}, nil 10 | } 11 | -------------------------------------------------------------------------------- /pkg/db/mongo/config.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | type Config struct{} 4 | -------------------------------------------------------------------------------- /pkg/db/mongo/mongo.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | type MongoDB struct{} 4 | 5 | // New ... 6 | func New(config *Config) (*MongoDB, error) { 7 | return &MongoDB{}, nil 8 | } 9 | -------------------------------------------------------------------------------- /pkg/db/mysql/config.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | type Config struct{} 4 | -------------------------------------------------------------------------------- /pkg/db/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | type MySql struct{} 4 | 5 | // New ... 6 | func New(config *Config) (*MySql, error) { 7 | return &MySql{}, nil 8 | } 9 | -------------------------------------------------------------------------------- /pkg/logger/config.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | var callerSkipNo int = 3 4 | 5 | // Config ... 6 | type Config struct { 7 | Env string 8 | AppName string 9 | AppVersion string 10 | DebugEnabled bool 11 | CallerSkipNo int 12 | } 13 | -------------------------------------------------------------------------------- /pkg/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "log/slog" 5 | "os" 6 | 7 | "github.com/lokesh-go/go-api-microservice/pkg/utils" 8 | ) 9 | 10 | type Logger struct { 11 | Log *slog.Logger 12 | } 13 | 14 | // New creates a new logger 15 | func New(config *Config) *Logger { 16 | // Initialise slog 17 | logHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ 18 | AddSource: true, 19 | Level: slog.LevelInfo, 20 | }) 21 | sLogger := slog.New(logHandler).With(utils.HostNameJSONKey, utils.GetHostname(), utils.EnvJSONKey, config.Env, utils.AppJSONKey, config.AppName, utils.AppVersionJSONKey, config.AppVersion) 22 | 23 | // Returns 24 | return &Logger{Log: sLogger} 25 | } 26 | -------------------------------------------------------------------------------- /pkg/utils/constants.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | const ( 4 | EnvKey = "ENV" 5 | EnvDefault = "dev" 6 | 7 | AppJSONKey = "app" 8 | AppVersionJSONKey = "appVersion" 9 | HostNameJSONKey = "hostname" 10 | EnvJSONKey = "env" 11 | StackJSONKey = "stack" 12 | ErrorJSONKey = "error" 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "gopkg.in/yaml.v3" 8 | ) 9 | 10 | // Get environment 11 | func GetEnv(envKey string) (env string) { 12 | return os.Getenv(envKey) 13 | } 14 | 15 | // GetHostname 16 | func GetHostname() (hostname string) { 17 | hostname, err := os.Hostname() 18 | if err != nil { 19 | return "" 20 | } 21 | 22 | // Returns 23 | return hostname 24 | } 25 | 26 | // IsEmpty check if the string is empty 27 | func IsEmpty(str string) bool { 28 | return strings.TrimSpace(str) == "" 29 | } 30 | 31 | // Read the file 32 | func ReadFile(filePath string) (data []byte, err error) { 33 | // Read file 34 | data, err = os.ReadFile(filePath) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | // Returns 40 | return data, nil 41 | } 42 | 43 | // YAML Unmarshal 44 | func YAMLUnmarshal(data []byte, v any) (err error) { 45 | return yaml.Unmarshal(data, v) 46 | } 47 | -------------------------------------------------------------------------------- /scripts/bump_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | PART=$1 6 | VERSION=$(cat VERSION) 7 | echo "Current version $VERSION" 8 | 9 | # strip leading 'v' if present 10 | VERSION=${VERSION#v} 11 | 12 | # splits version into components 13 | IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" 14 | 15 | # bump the version 16 | case "$PART" in 17 | major) 18 | MAJOR=$((MAJOR + 1)) 19 | MINOR=0 20 | PATCH=0 21 | ;; 22 | minor) 23 | MINOR=$((MINOR + 1)) 24 | PATCH=0 25 | ;; 26 | patch) 27 | PATCH=$((PATCH + 1)) 28 | ;; 29 | *) 30 | echo "Usage: bump_version.sh patch|minor|major" 31 | exit 1 32 | ;; 33 | esac 34 | 35 | # Reassemble and add 'v' prefix 36 | NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}" 37 | echo "$NEW_VERSION" > VERSION 38 | echo "Version bumped to $NEW_VERSION" -------------------------------------------------------------------------------- /test/server_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/lokesh-go/go-api-microservice/internal/bootstrap" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | // TestServer ... 12 | func TestServer(t *testing.T) { 13 | // Start the server 14 | go func() { 15 | err := bootstrap.Initialize() 16 | if err != nil { 17 | assert.NoError(t, err, "app initialisation failed") 18 | } 19 | }() 20 | 21 | t.Run("should return public server 200 OK", func(t *testing.T) { 22 | res, err := http.Get("http://localhost/ping") 23 | assert.NoError(t, err, "public server failed") 24 | defer res.Body.Close() 25 | assert.Equal(t, http.StatusOK, res.StatusCode, "expected public server 200") 26 | }) 27 | 28 | t.Run("should return internal server 200 OK", func(t *testing.T) { 29 | res, err := http.Get("http://localhost:8080/ping") 30 | assert.NoError(t, err, "internal server failed") 31 | defer res.Body.Close() 32 | assert.Equal(t, http.StatusOK, res.StatusCode, "expected internal server 200") 33 | }) 34 | } 35 | --------------------------------------------------------------------------------