feat: first
This commit is contained in:
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# Generated protobuf files
|
||||||
|
proto/**/*.pb.go
|
||||||
|
proto/**/*.pb.gw.go
|
||||||
|
|
||||||
|
# Buf lock file
|
||||||
|
buf.lock
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
68
README.md
Normal file
68
README.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Go gRPC Gateway Template
|
||||||
|
|
||||||
|
A simplified Go project template for gRPC services with HTTP/JSON gateway support, based on the [Go Standard Project Layout](https://github.com/golang-standards/project-layout).
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
├── cmd/server/ # Main application entry point
|
||||||
|
├── internal/
|
||||||
|
│ ├── handler/ # gRPC and gateway handlers
|
||||||
|
│ └── service/ # Business logic
|
||||||
|
├── pkg/pb/ # Generated protobuf files (auto-generated)
|
||||||
|
├── api/v1/ # Protocol buffer definitions
|
||||||
|
└── justfile # Task runner configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Go 1.21+
|
||||||
|
- Protocol Buffers compiler (`protoc`)
|
||||||
|
- [just](https://github.com/casey/just) task runner
|
||||||
|
- [nushell](https://www.nushell.sh/) (for justfile execution)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
```bash
|
||||||
|
just deps
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Generate protobuf files:
|
||||||
|
```bash
|
||||||
|
just proto
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Build and run the server:
|
||||||
|
```bash
|
||||||
|
just dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Commands
|
||||||
|
|
||||||
|
- `just proto` - Generate protobuf files
|
||||||
|
- `just build` - Build the server binary
|
||||||
|
- `just run` - Run the server
|
||||||
|
- `just dev` - Full development workflow (deps + proto + run)
|
||||||
|
- `just test` - Run tests
|
||||||
|
- `just lint` - Run linter
|
||||||
|
- `just clean` - Clean build artifacts
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
The server runs on two ports:
|
||||||
|
- gRPC server: `:8080`
|
||||||
|
- HTTP gateway: `:8081`
|
||||||
|
|
||||||
|
### Example HTTP endpoints:
|
||||||
|
- `GET /v1/examples` - List all examples
|
||||||
|
- `POST /v1/examples` - Create a new example
|
||||||
|
- `GET /v1/examples/{id}` - Get a specific example
|
||||||
|
|
||||||
|
## Monorepo Integration
|
||||||
|
|
||||||
|
This template is designed to be integration-friendly for monorepo structures by:
|
||||||
|
- Excluding shared proto folders
|
||||||
|
- Using internal packages for service-specific logic
|
||||||
|
- Minimal external dependencies
|
||||||
|
- Clear separation of concerns
|
||||||
17
buf.gen.yaml
Normal file
17
buf.gen.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
version: v2
|
||||||
|
plugins:
|
||||||
|
# generate go structs for protocol buffer definition
|
||||||
|
- local: protoc-gen-go
|
||||||
|
out: .
|
||||||
|
opt:
|
||||||
|
- paths=source_relative
|
||||||
|
# generate gRPC stubs in golang
|
||||||
|
- local: protoc-gen-go-grpc
|
||||||
|
out: .
|
||||||
|
opt:
|
||||||
|
- paths=source_relative
|
||||||
|
# generate reverse proxy from protocol definitions
|
||||||
|
- local: protoc-gen-grpc-gateway
|
||||||
|
out: .
|
||||||
|
opt:
|
||||||
|
- paths=source_relative
|
||||||
10
buf.yaml
Normal file
10
buf.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: v2
|
||||||
|
name: buf.build/grpc-gateway-template/proto
|
||||||
|
deps:
|
||||||
|
- buf.build/googleapis/googleapis
|
||||||
|
breaking:
|
||||||
|
use:
|
||||||
|
- FILE
|
||||||
|
lint:
|
||||||
|
use:
|
||||||
|
- DEFAULT
|
||||||
84
cmd/server/main.go
Normal file
84
cmd/server/main.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
|
helloworldpb "go-grpc-gateway-template/proto/helloworld"
|
||||||
|
)
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
helloworldpb.UnimplementedGreeterServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) SayHello(ctx context.Context, req *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
|
||||||
|
return &helloworldpb.HelloReply{
|
||||||
|
Message: "Hello " + req.GetName(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a listener on TCP port
|
||||||
|
lis, err := net.Listen("tcp", ":8080")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to listen:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a gRPC server object
|
||||||
|
s := grpc.NewServer()
|
||||||
|
// Attach the Greeter service to the server
|
||||||
|
helloworldpb.RegisterGreeterServer(s, &server{})
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
// Serve gRPC server
|
||||||
|
log.Println("Serving gRPC on 0.0.0.0:8080")
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := s.Serve(lis); err != nil {
|
||||||
|
log.Fatalln("Failed to serve:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create a client connection to the gRPC server we just started
|
||||||
|
// This is where the gRPC-Gateway proxies the requests
|
||||||
|
conn, err := grpc.DialContext(
|
||||||
|
context.Background(),
|
||||||
|
"0.0.0.0:8080",
|
||||||
|
grpc.WithBlock(),
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to dial server:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gwmux := runtime.NewServeMux()
|
||||||
|
// Register Greeter
|
||||||
|
err = helloworldpb.RegisterGreeterHandler(context.Background(), gwmux, conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to register gateway:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gwServer := &http.Server{
|
||||||
|
Addr: ":8090",
|
||||||
|
Handler: gwmux,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090")
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := gwServer.ListenAndServe(); err != nil {
|
||||||
|
log.Fatalln("Failed to serve gRPC-Gateway:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
19
go.mod
Normal file
19
go.mod
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module go-grpc-gateway-template
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.23.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822
|
||||||
|
google.golang.org/grpc v1.74.2
|
||||||
|
google.golang.org/protobuf v1.36.6
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/net v0.40.0 // indirect
|
||||||
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
golang.org/x/text v0.26.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||||
|
)
|
||||||
38
go.sum
Normal file
38
go.sum
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
|
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||||
|
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||||
|
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||||
|
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||||
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
|
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||||
|
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||||
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
55
justfile
Normal file
55
justfile
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
# Update buf dependencies
|
||||||
|
buf-deps:
|
||||||
|
buf dep update
|
||||||
|
|
||||||
|
# Generate protobuf files
|
||||||
|
proto: buf-deps
|
||||||
|
buf generate
|
||||||
|
|
||||||
|
# Build the server binary
|
||||||
|
build: proto
|
||||||
|
go build -o bin/server cmd/server/main.go
|
||||||
|
|
||||||
|
# Run the server
|
||||||
|
run: build
|
||||||
|
./bin/server
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
clean:
|
||||||
|
rm -rf bin/
|
||||||
|
rm -rf pkg/pb/
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
test:
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run linter
|
||||||
|
lint:
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
|
# Install required tools and dependencies
|
||||||
|
install-deps:
|
||||||
|
@echo "Installing buf..."
|
||||||
|
@if ! command -v buf >/dev/null 2>&1; then \
|
||||||
|
mkdir -p ~/.local/bin && \
|
||||||
|
curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-$(uname -s)-$(uname -m)" -o "$HOME/.local/bin/buf" && \
|
||||||
|
chmod +x "$HOME/.local/bin/buf"; \
|
||||||
|
echo "buf installed to ~/.local/bin/buf"; \
|
||||||
|
echo "Make sure ~/.local/bin is in your PATH"; \
|
||||||
|
else \
|
||||||
|
echo "buf already installed"; \
|
||||||
|
fi
|
||||||
|
@echo "Installing protoc plugins..."
|
||||||
|
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||||
|
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||||
|
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
|
||||||
|
|
||||||
|
# Download and tidy dependencies
|
||||||
|
deps:
|
||||||
|
go mod download
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
# Development workflow
|
||||||
|
dev: deps proto run
|
||||||
24
proto/helloworld/hello_world.proto
Normal file
24
proto/helloworld/hello_world.proto
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option go_package = "go-grpc-gateway-template/proto/helloworld";
|
||||||
|
|
||||||
|
package helloworld;
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
|
service Greeter {
|
||||||
|
rpc SayHello (HelloRequest) returns (HelloReply) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v1/example/echo"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelloRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelloReply {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user