I put together a simple example and all the boilerplate needed to get started with a gRPC service in Go. The following Git repository includes some of what I consider best practices, useful middleware, observability, and other features.
Git Repository: grpc-go-boilerplate
The following Git repository includes a “Hello world” gRPC service with multiple features and best practices. Please feel free to clone, try, and modify it per your needs.
Features
I have included multiple features in this project, some of which took me considerable time to figure out and implement. Going forward, starting new gRPC services should be much quicker given this boilerplate.
Example gRPC Service
The Hello service is quite a simple service which does not take any input, and calling it simply returns a string. The service definitions are inside the proto
directory and implementation inside the internal/hello
directory.
Protobuf Compilation
Protobuf compilation is built right into the Go project with buf
, so no external dependencies like installing the protoc
compiler are needed on the system. Compile the protobuf definitions by either running make generate
or go generate ./...
. For more information regarding how everything fits together refer to the blog post linked at the end, and the buf
documentation.
Structured Logging & Logging Middleware
The project leverages zerolog
for its logging library. It is my favorite Go logging library and I recommend using it because of its features and performance. When in dev
mode the logging is setup to be more human readable and set to JSON otherwise for production deployments.
Panic Recovery Interceptor
Included is a panic recovery interceptor middleware that turns panics into gRPC errors. The recovery interceptor is a middleware that recovers from panics and logs the panic message–the last thing we want is a panic to bring down our entire server.
Prometheus Interceptor
To improve observability and metrics included is the Prometheus interceptor middleware that adds gRPC specific metrics. Prometheus metrics are available on the :2112/metrics
endpoint when the service is running.
Health Check Service
An example health check service is provided for health checks, and readiness + liveliness probes if deploying on Kubernetes. The Hello service health service implementation is very basic, and in a real scenario some more logic needs to be implemented depending on your service.
buf
& Plugins Integration
buf
is a very useful tool when working with Protobuf. It helps with things like compilation, linting, breaking change detection, and more. Configuration can be found inside the buf.yaml
and buf.gen.yaml
files. Refer to their documentation for further details.
Prometheus Metrics
Example setup of Prometheus Metrics. Various Prometheus metrics are available on the :2112/metrics
endpoint when the service is running.
Versioned APIs
It is a good idea to version our APIs to support us going forward with introducing breaking changes. The proto files are structured inside the proto
folder as follows: service/vN/file.proto
. And the package definition inside the protobuf files: package service.vN;
Dev Mode Toggle
Sometimes it is useful to run your service locally during development. This could mean mocking out certain dependencies, services or what not. This can be done by providing a -dev
argument when running the service to allow for branching and handling of such cases. Refer to the main
function for an example.
Protobuf & Code Linting
Go code linting is handled by golangci-lint and protobuf linting via the buf tool. Run make lint
to run both and see if there are any problems detected with your code or protobuf files.
Graceful Shutdown
The service can be stopped by issuing an interrupt (Ctrl+C), after which any additional internal services can be notified (via a shared Context
) and are given 5 seconds to perform any clean-up. When either all additional internal services complete their cleanup, 5 seconds pass, or another interrupt is issued, the main service terminates. The time can be configured, and the basic framework can be found inside cmd/grpc-go-boilerplate/main.go
.
Makefile
The Makefule
provides several useful targets for things like compiling our protobuf and running the code/proto lint. Review the file for more details.
Dockerfile
The file docker/Dockerfile
demonstrates how to build a docker image of our service, including pulling dependencies and compiling our protobuf.
gRPC Gateway Integration
gRPC Gateway is a protoc
plugin that reads a gRPC service definition and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. The google.api.http
option service annotations inside the hello.proto
file are used with this plugin. If your project has the need to use such a plugin, it is included in this project, and we will see how to use it in a section below.
No Generated Code Committed
It is a preference of mine not to have generated code be committed inside the Git repository. For that reason, any generated code for the project results in the gen
directory, and that directory is Git ignored.
Run Server
Let’s look at an example of running the service and calling it with a client. Download all project dependencies, compile protobuf, and run:
go mod download all
make generate # or go generate ./...
go run cmd/grpc-go-boilerplate/main.go -dev
... INF gRPC server listening on :8080
... INF metrics available on :2112/metrics
... INF gRPC Gateway listening on :8081
Run Client
Call our Hello gRPC service:
grpcurl --plaintext localhost:8080 hello.v1.HelloService/Hello
{
"hello": "Hello world!"
}
Call the service using the gRPC Gateway:
curl localhost:8081/v1/hello
{"hello":"Hello world!"}
Calling the health service using grpc-health-probe
:
grpc-health-probe -addr ":8080" -service="hello.v1.HelloService"
status: SERVING
grpc-health-probe -addr ":8080"
status: SERVING