A production-ready Go microservice implementing a Pet Store API with modern observability and best practices.
- Go 1.24+ - Latest Go version with modern language features
- gRPC - High-performance RPC framework with HTTP/2
- gRPC-Gateway - Automatic REST API generation from gRPC definitions
- Protocol Buffers - Efficient serialization and API-first design
- OpenTelemetry - Distributed tracing and metrics with exponential histograms
- Structured Logging - High-performance structured logging using
log/slog
- JWT-based Authentication: Secure token generation and validation.
- Refresh Tokens: Mechanism for obtaining new access tokens without re-authentication.
- gRPC Interceptors: Centralized authentication and role-based authorization for gRPC endpoints.
- Password Hashing: Secure password storage using bcrypt.
- Health Checks - Standard gRPC health check protocol
- Request Logging - Structured logging with request/response details
- Auto-ID Generation - UUIDs automatically generated when not provided
- Input Validation - Comprehensive validation with proper error codes
- Graceful Shutdown - Clean service shutdown on interrupt signals
- Keep-Alive Settings - Production-ready connection management
- gRPC Reflection - Service discovery for development and debugging
- Thread Safety - Concurrent-safe operations with proper mutex usage
- Docker & Docker Compose - Containerized deployment with observability stack
- Code Generation - Automated protobuf code generation
- Testing - Comprehensive unit tests with race detection
- Linting - Code quality enforcement with golangci-lint
- Go 1.24 or later - Download from golang.org
- Protocol Buffers compiler (
protoc) - Required for code generation
- Docker and Docker Compose - For containerized deployment
- grpcurl - For testing gRPC endpoints (install guide)
-
Install all dependencies at once:
make install-deps
This will:
- Check if
protocis installed (and provide installation instructions if not) - Install all Go development tools (protoc plugins, linter, etc.)
- Download all Go module dependencies
- Check if
-
Manual installation of protoc (if needed):
macOS:
brew install protobuf
Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y protobuf-compilerFedora:
sudo dnf install protobuf-compiler
Other platforms: Download from GitHub releases
-
Clone and enter the project:
git clone <repo-url> cd gopherservice
-
Install all dependencies:
make install-deps # Installs protoc and all Go tools -
Generate code and prepare project:
make generate # Generate Go code from .proto files make tidy # Clean up Go modules
-
Build and test:
make build # Build the service make test # Run unit tests make lint # Run code quality checks
-
Complete build pipeline:
make build-all # Run deps, generate, build, test, and lint
To verify that all dependencies are properly installed:
# Check Go version
go version
# Check protoc version
protoc --version
# Check that all Go tools are installed
which protoc-gen-go protoc-gen-go-grpc protoc-gen-grpc-gateway golangci-lint
# Test complete build pipeline
make build-allCommon Issues:
-
protoc: command not found- Run
make install-protocfor installation instructions - Ensure protoc is in your PATH
- Run
-
protoc-gen-go: command not found- Run
make depsto install Go protoc plugins - Ensure
$GOPATH/binor$HOME/go/binis in your PATH
- Run
-
Permission denied when installing protoc
- On Linux/macOS: Use
sudowith package managers - Or install to user directory without sudo
- On Linux/macOS: Use
-
Go module issues
- Run
make tidyto clean up dependencies - Ensure you're using Go 1.24 or later
- Run
Getting Help:
- Run
make helpto see all available targets - Check that
go env GOPATHandgo env GOBINare set correctly
make buildgo build -o examples/grpc/grpc-client ./examples/grpc
go build -o examples/http/http-client ./examples/httpStart the service locally:
make runService Endpoints:
- gRPC Server:
localhost:8080 - HTTP/REST API:
localhost:8081
Run with full observability stack:
docker-compose up --buildAvailable Services:
- gRPC Server:
localhost:8080 - HTTP/REST API:
localhost:8081 - OpenTelemetry Collector:
localhost:4317
Configure the service with these environment variables:
export GRPC_PORT=8080 # gRPC server port
export HTTP_PORT=8081 # HTTP gateway port
export JWT_SECRET_KEY="your-secret-key" # Secret key for JWT signing (REQUIRED)Run all tests with coverage:
make testTest for race conditions:
go test -race ./...Run linting and formatting checks:
make lintRun the entire build and test pipeline:
make build-allTo run the gRPC and HTTP examples, you need to start the gopherservice server manually in a separate terminal, and then run the client examples.
-
Start the
gopherserviceserver in a separate terminal:JWT_SECRET_KEY="supersecretjwtkey" DATABASE_DSN="/tmp/gopherservice.db" OTEL_EXPORTER_OTLP_ENDPOINT="" ./gopherservice
(Keep this terminal open while running the client examples.)
-
In a new terminal, run the gRPC client example:
go run ./examples/grpc
-
In another new terminal, run the HTTP client example:
go run ./examples/http
-
To stop the
gopherserviceserver, pressCtrl+Cin the terminal where it's running.
Register a User:
curl -X POST http://localhost:8081/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "testuser@example.com",
"password": "password123",
"full_name": "Test User",
"roles": ["user"]
}'Login and Get Token:
curl -X POST http://localhost:8081/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"credentials": {
"email": "testuser@example.com",
"password": "password123"
}
}'
# Save the access_token and refresh_token from the responseRefresh Token:
curl -X POST http://localhost:8081/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "YOUR_REFRESH_TOKEN"
}'
# Get a new access_tokenCreate a Pet (requires authentication):
curl -X POST http://localhost:8081/v1/pets \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"pet": {
"name": "Buddy",
"species": "Dog"
}
}'Get a Pet (requires authentication):
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
http://localhost:8081/v1/pets/{pet-id}Place an Order (requires authentication):
curl -X POST http://localhost:8081/v1/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"order": {
"petId": "{pet-id}",
"quantity": 1
}
}'Get an Order (requires authentication):
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
http://localhost:8081/v1/orders/{order-id}Use tools like grpcurl or evans to interact with the gRPC API. For authenticated calls, you'll need to pass the JWT access token in the metadata.
Login and Get Token (gRPC):
grpcurl -plaintext -d '{
"credentials": {
"email": "testuser@example.com",
"password": "password123"
}
}' localhost:8080 v1.AuthService/Login
# Save the access_token and refresh_token from the responseCreate a Pet (requires authentication via gRPC):
grpcurl -plaintext \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"pet": {
"name": "Buddy",
"species": "Dog"
}
}' localhost:8080 v1.PetStoreService/CreatePetHealth check:
grpcurl -plaintext localhost:8080 grpc.health.v1.Health/CheckCreate a pet (old example, now requires auth):
grpcurl -plaintext -d '{
"pet": {
"name": "Buddy",
"species": "Dog"
}
}' localhost:8080 v1.PetStoreService/CreatePet┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ HTTP Client │ │ gRPC Client │ │ Health Checks │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ gRPC-Gateway │ │ gRPC Server │ │ gRPC Reflection │
│ (REST Proxy) │ │ │ │ │
│ Port: 8081 │ │ Port: 8080 │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
└────────────────────────┼────────────────────────┘
│
▼
┌──────────────────┐
│ PetStore Service │
│ (Business Logic) │
└──────────────────┘
│
▼
┌──────────────────┐
│ In-Memory │
│ Storage │
│ (Thread-Safe) │
└──────────────────┘
- Traces: OpenTelemetry → OTLP Collector
- Metrics: Exponential histograms for latency/duration
- Logs: Structured JSON with
log/slog - Health: Standard gRPC health checks
- API-First: Protocol buffers define the contract
- Clean Architecture: Clear separation of concerns
- Dual Protocol: Single implementation serves gRPC + REST
- Thread Safety: Concurrent operations with proper synchronization
The service includes comprehensive observability:
Tracing:
- Automatic gRPC request tracing
- Distributed trace propagation
- Request/response logging with correlation IDs
Metrics:
- Exponential histograms for key service metrics
- gRPC server duration, latency, errors, and payload size
- Delta temporality for efficient metric collection
Health Checks:
# Check overall service health
grpcurl -plaintext localhost:8080 grpc.health.v1.Health/Check
# Check specific service health
grpcurl -plaintext -d '{"service":"v1.PetStoreService"}' \
localhost:8080 grpc.health.v1.Health/CheckStructured JSON logging with:
- Request method and duration
- gRPC status codes
- Error details and stack traces
- Business operation context (pet/order IDs)
gopherservice/
├── api/v1/ # Protocol buffer definitions
├── cmd/server/ # Main application entry point
├── internal/ # Private application code
│ ├── config/ # Configuration loading
│ ├── log/ # Logger initialization
│ ├── petstore/ # Business logic implementation
│ └── server/grpc/ # gRPC server setup
├── pkg/telemetry/ # Shared telemetry utilities
├── examples/ # Client examples
│ ├── grpc/ # gRPC client example
│ └── http/ # HTTP client example
└── third_party/ # External dependencies
Protocol buffer code is automatically generated:
make generate # Generates Go code from .proto filesGenerated files:
api/v1/petstore.pb.go- Protocol buffer typesapi/v1/petstore_grpc.pb.go- gRPC service definitionsapi/v1/petstore.pb.gw.go- gRPC-Gateway REST mappings
| Command | Description |
|---|---|
make install-deps |
Install all system and Go dependencies |
make install-protoc |
Check/install Protocol Buffers compiler |
make deps |
Install Go development tools only |
make generate |
Generate code from proto files |
make build |
Build the service executable |
make test |
Run unit tests with coverage |
make lint |
Run code quality checks |
make run |
Start the service locally |
make run-examples |
Build and run example clients |
make build-all |
Complete build pipeline |
make clean |
Remove build artifacts |
make tidy |
Clean up Go modules |
- Make changes to
.protofiles for API modifications - Run
make generateto update generated code - Implement business logic in
internal/petstore/ - Add tests and run
make test - Verify with
make build-all
Build and run with Docker:
docker build -t gopherservice .
docker run -p 8080:8080 -p 8081:8081 gopherserviceFull stack with observability:
docker-compose up -dThis includes:
- Gopherservice (gRPC + HTTP)
- OpenTelemetry Collector
- Example telemetry configuration
Kubernetes configurations are provided in the kubernetes/ directory.
Generated Files:
kubernetes/otel-collector-configmap.yaml: ConfigMap for OpenTelemetry collector configuration.kubernetes/otel-collector-deployment-service.yaml: Deployment and Service for the OpenTelemetry collector.kubernetes/gopherservice-deployment-service.yaml: Deployment and Service for thegopherserviceapplication.
Deployment Steps:
- Ensure your Docker image
gopherservice:latestis available in your Kubernetes cluster's image registry. If you're using a local Kubernetes (like Minikube or Kind), you might need to load the image into its daemon:docker build -t gopherservice . # If using Minikube: minikube image load gopherservice:latest # If using Kind: kind load docker-image gopherservice:latest
- Apply the configurations:
kubectl apply -f kubernetes/otel-collector-configmap.yaml kubectl apply -f kubernetes/otel-collector-deployment-service.yaml kubectl apply -f kubernetes/gopherservice-deployment-service.yaml
- Check the status of your deployments:
kubectl get pods kubectl get services
Further Considerations for Kubernetes:
- Ingress: For external access to your HTTP/REST API.
- Secrets: For sensitive information like
JWT_SECRET_KEYinstead of hardcoding them or passing them as environment variables directly in the Deployment YAML. - Resource Limits/Requests: To define CPU and memory limits for your pods.
- Horizontal Pod Autoscaler (HPA): For automatic scaling based on metrics.
- Persistent Volume Claims (PVCs): If your
gopherserviceneeds persistent storage (currently it uses a file-based SQLite in/tmp, which is ephemeral in a container).
Service configuration via config.yaml:
telemetry:
serviceName: "gopherservice"
endpoint: "localhost:4317"Environment variable overrides:
GRPC_PORT: gRPC server port (default: 8080)HTTP_PORT: HTTP gateway port (default: 8081)
This project exemplifies Go microservice best practices with:
- Go 1.24+: Latest language features and performance improvements
- Clean Architecture: Clear separation with
internal/andpkg/structure - Interface-Based Design: Dependency injection ready architecture
- Thread Safety: Proper mutex usage for concurrent operations
- Context Propagation: Request-scoped contexts throughout the call chain
- Production-Ready Server: Keepalive, health checks, and graceful shutdown
- OpenTelemetry Integration: Automatic tracing and metrics collection
- gRPC-Gateway: Seamless HTTP/REST API from protobuf definitions
- Service Reflection: Development and debugging support
- Error Handling: Proper gRPC status codes and error propagation
- Distributed Tracing: OpenTelemetry with OTLP export
- Metrics: Exponential histograms for performance monitoring
- Structured Logging: High-performance
log/slogwith correlation - Health Checks: Standard gRPC health check protocol
- Code Generation: Automated protobuf compilation
- Comprehensive Testing: Unit tests with 87%+ coverage
- Quality Gates: golangci-lint integration
- Docker Support: Multi-stage builds and compose orchestration
- Example Clients: Working gRPC and HTTP examples
Copyright 2025 Phillip Lindsay
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.