A microservice project built with gRPC and Go, using Gin as the HTTP API Gateway.
Client (REST/JSON)
│
▼
API Gateway (Gin) :8080
│ gRPC (protobuf)
├──────────────────────┐
▼ ▼
user-service :50051 product-service :50052
Communication between services uses gRPC internally, while the API Gateway exposes a REST HTTP interface to the outside world.
simple-go-grpc/
│
├── pb/ # Generated protobuf files
│
├── proto/ # Protobuf definitions
│ ├── user.proto
│ └── product.proto
│
├── service/
│ ├── user/ # User gRPC Service (port 50051)
│ │ ├── main.go
│ │ ├── server/
│ │ │ ├── user_server.go # gRPC handler implementation
│ │ │ └── interceptors.go # Logging & recovery interceptors
│ │ └── repository/
│ │ └── user_repo.go
│ │
│ └── product/ # Product gRPC Service (port 50052)
│ ├── main.go
│ ├── server/
│ │ ├── product_server.go
│ │ └── interceptors.go
│ └── repository/
│ └── product_repo.go
│
├── api/ # API Gateway (port 8080)
│ ├── main.go
│ ├── middleware/
│ │ ├── auth.go # JWT validation middleware
│ │ └── rate_limiter.go # Rate limiting per IP
│ ├── handler/
│ │ ├── auth_handler.go # POST /auth/register, /auth/login
│ │ ├── user_handler.go # GET /users/:id
│ │ └── product_handler.go # CRUD /products
│ └── client/
│ ├── user_client.go # gRPC client wrapper
│ └── product_client.go
│
├── Dockerfile # Single Dockerfile with ENTRY_PATH arg
├── docker-compose.yml
├── Makefile
└── go.mod
- Go 1.24
- gRPC — internal service communication
- Gin — HTTP API Gateway
- Protobuf — service contracts & serialization
- JWT — authentication
- Docker — containerization
# Install protoc
# macOS
brew install protobuf
# Ubuntu
apt-get install -y protobuf-compiler
# Windows
1. Download protoc from https://github.com/protocolbuffers/protobuf/releases
2. Extract and copy protoc.exe to a folder in your PATH
example: C:\Program Files\protoc\bin
3. Add the folder to System Environment Variables > PATH
# Install Go plugins
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# Add to PATH
export PATH="$PATH:$(go env GOPATH)/bin"docker-compose up --build# Terminal 1 — user service
go run ./service/user/main.go
# Terminal 2 — product service
go run ./service/product/main.go
# Terminal 3 — api gateway
go run ./api/main.go# Use Makefile
make proto
# Or manual
protoc \
--proto_path=proto \
--go_out=pb --go_opt=paths=source_relative \
--go-grpc_out=pb --go-grpc_opt=paths=source_relative \
user.proto \
product.protoThe generated results will go into the pb/ folder and produce:
*.pb.go— struct for every message*_grpc.pb.go— interface server & client stub
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Register new user |
| POST | /api/v1/auth/login |
Login & get JWT token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/users/:id |
Get user by ID |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/products |
Create product |
| GET | /api/v1/products |
List products (paginated) |
| GET | /api/v1/products/:id |
Get product by ID |
All protected routes require Authorization: Bearer <token> header.
# Register
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"name":"Budi","email":"budi@test.com","password":"password123"}'
# Login
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"budi@test.com","password":"password123"}'
# Create product (use token from login response)
curl -X POST http://localhost:8080/api/v1/products \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"Laptop","price":15000000,"stock":10}'
# List products
curl http://localhost:8080/api/v1/products?page=1&limit=10 \
-H "Authorization: Bearer <token>"Why gRPC for internal services?
- Faster than REST — uses binary protobuf instead of JSON
- Strict contracts — proto files enforce API contracts at compile time
- Not suitable for browser/frontend — use REST at the gateway layer instead
Proto as a contract
.proto files define the message structures and service interfaces. After code generation, they produce Go structs, server interfaces, and client stubs — but database logic, error handling, and business logic are still written manually.
Single Dockerfile
All services share one Dockerfile, differentiated by the ENTRY_PATH build argument:
args:
ENTRY_PATH: service/user # or service/product, api