Building a REST/gRPC Service Using the Go-Kratos Framework
In the dynamic world of software development, the choice of framework can be crucial to the success of your project. For developers in the Go ecosystem, go-kratos offers a robust and flexible solution for building REST and gRPC services. This blog will guide you through creating a REST/gRPC service with go-kratos, highlight the business benefits, and assess the technical pros and cons.
Why Choose Go-Kratos?
Go-Kratos promotes an approach that encourages developers to think of their APIs in terms of Protocol Buffers (Protobuf). This helps to establish a consistent source of truth for both gRPC and RESTful APIs. This design philosophy supports a contract-first development process and enables the automatic generation of OpenAPI specifications.
1. Protobuf as the Source of Truth
The API design process in go-kratos starts with defining service interfaces and data models using Protobuf. This method ensures that the API agreements are well-defined and consistent across various communication protocols. Protobuf allows you to determine your gRPC services and messages in a language-agnostic format, which can then be used to generate client and server code in multiple programming languages.
Here’s a brief outline of how this process works:
- Define Your API in Protobuf:
- Write your service definitions and messages using Protobuf syntax.
- This step is crucial as it is the blueprint for gRPC and REST APIs.
2. Generate Go Code:
- Go-Kratos uses standard tools such as https://grpc.io/docs/protoc-installation/ to generate Go code from the Protobuf definitions. The generated code will include the necessary gRPC server, client stubs, and HTTP/REST endpoints handlers.
3. Generate OpenAPI Specification:
- Go-Kratos can automatically generate an OpenAPI specification from your Protobuf definitions. This is particularly beneficial as it lets you have up-to-date API documentation reflecting the implementation.
- The OpenAPI specification can generate client SDKs, produce API documentation, or integrate with API gateways.
2. Rapid Development and Time-to-Market
- Productivity: Go-Kratos supports common patterns and best practices, allowing teams to focus on business logic and accelerate development cycles for faster time to market.
3. Flexibility in Communication Protocols:
- Go-Kratos supports REST and gRPC, allowing businesses to choose the most suitable communication protocol according to their needs. REST is widely adopted and ideal for web services, while gRPC offers high performance and is well-suited for internal service-to-service communication.
4. Reduced Technical Debt
- Supported by a robust open-source community, it uses
MIT License
, Go-Kratos receives continuous updates, reducing the risk of using outdated or poorly supported frameworks.
5. Middleware support
- Go-Kratos provides a standard set of middleware for authentication, logging, validation, observability, recovery, etc. Which enables rapid development and focus on business development over-focus on the technical debt.
6. Dependency Injection using Google’s Wire
- By incorporating Google’s Wire, go-Kratos ensures type-safe dependency injection in Go, enabling developers to manage and inject dependencies with enhanced structuring efficiently.
- Go-Kratos promotes a modular design, encouraging loose coupling of different components such as servers, databases, and services.
Building a REST/gRPC Service
Step 1: Project Setup
- Start by installing the Go-Kratos CLI tool and creating a new project:
go env -w GO111MODULE=on
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
Create a new project using the Go-Kratos CLI tool
kratos new demo-service
On successful generation, you will see the output below:
🚀 Creating service demo-service, layout repo is https://github.com/go-kratos/kratos-layout.git, please wait a moment.
CREATED demo-service/.gitignore (552 bytes)
CREATED demo-service/Dockerfile (459 bytes)
CREATED demo-service/LICENSE (1066 bytes)
CREATED demo-service/Makefile (2478 bytes)
CREATED demo-service/api/helloworld/v1/greeter.proto (680 bytes)
...
CREATED demo-service/configs/config.yaml (291 bytes)
....
CREATED demo-service/internal/server/server.go (150 bytes)
CREATED demo-service/internal/service/README.md (10 bytes)
CREATED demo-service/internal/service/greeter.go (692 bytes)
CREATED demo-service/internal/service/service.go (136 bytes)
CREATED demo-service/openapi.yaml (1130 bytes)
CREATED demo-service/third_party/README.md (14 bytes)
🍺 Project creation succeeded demo-service
💻 Use the following command to start the project 👇:
$ cd demo-service
$ go generate ./...
$ go build -o ./bin/ ./...
$ ./bin/demo-service -conf ./configs
🤝 Thanks for using Kratos
📚 Tutorial: https://go-kratos.dev/docs/getting-started/start
Step 2: Quick Build and Run
Using command kratos run
will start the demo service. Exposes a sample default Greeter
service listening on REST at the port :8000
and on gRPC at the port :9000
.
$ cd demo-service
$ kratos run
go: downloading go1.22.6 (darwin/arm64)
2024/08/22 22:15:03 maxprocs: Leaving GOMAXPROCS=10: CPU quota undefined
DEBUG msg=config loaded: config.yaml format: yaml
INFO ts=2024-08-22T22:15:03+05:30 caller=http/server.go:330 service.id=akoserwa1-mac service.name= service.version= trace.id= span.id= msg=[HTTP] server listening on: [::]:8000
INFO ts=2024-08-22T22:15:03+05:30 caller=grpc/server.go:212 service.id=akoserwa1-mac service.name= service.version= trace.id= span.id= msg=[gRPC] server listening on: [::]:9000
Let’s take a quick test for HTTP and gRPC endpoints.
For HTTP, using the command line curl
, the client
$ curl http://localhost:8000/helloworld/test
{
"message": "Hello test"
}
For gRPC, using the command line grpcurl
$ grpcurl -d '{"name": "test"}' -plaintext localhost:9000 helloworld.v1.Greeter.SayHello
{
"message": "Hello test"
}
In the above case, HTTP and gRPC requests are made to SayHello
method of the Greeter
service.
Proto's definition of Greeter service
syntax = "proto3";
package helloworld.v1;
import "google/api/annotations.proto";
option go_package = "demo-service/api/helloworld/v1;v1";
option java_multiple_files = true;
option java_package = "dev.kratos.api.helloworld.v1";
option java_outer_classname = "HelloworldProtoV1";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/helloworld/{name}"
};
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
Step 3: Add a new proto-definition
To add a new proto definition, we can use kratos proto add
command
kratos proto add api/testdomain/v1/demo.proto
The generated proto definition will be under api
directory
$ api tree
.
├
└── testdomain
└── v1
├── demo.proto
Step 4: Let’s update the Demo Proto definition.
syntax = "proto3";
package api.testdomain.v1;
import "google/api/annotations.proto";
option go_package = "demo-service/api/testdomain/v1;v1";
option java_multiple_files = true;
option java_package = "api.testdomain.v1";
service Demoservice {
rpc CreateDemo (CreateDemoRequest) returns (CreateDemoReply) {
option (google.api.http) = {
post: "/api/testdomain/v1/demo"
body : "*"
};
};
rpc UpdateDemo (UpdateDemoRequest) returns (UpdateDemoReply);
rpc DeleteDemo (DeleteDemoRequest) returns (DeleteDemoReply) {
option (google.api.http) = {
delete: "/api/testdomain/v1/demo"
};
};
rpc GetDemo (GetDemoRequest) returns (GetDemoReply) {
option (google.api.http) = {
get: "/api/testdomain/v1/demo/{name}"
};
};
rpc ListDemo (ListDemoRequest) returns (ListDemoReply) {
option (google.api.http) = {
get: "/api/testdomain/v1/demo"
};
};
}
Once we have made updates to the proto-definition
Step 5: Generate the Proto codes using make target
$ make all
The above command will use protoc
compiler to generate the proto codes, and it re-generates configuration and wire.
Output:
demo-service make all
make api;
protoc --proto_path=./api \
--proto_path=./third_party \
--go_out=paths=source_relative:./api \
--go-http_out=paths=source_relative:./api \
--go-grpc_out=paths=source_relative:./api \
--openapi_out=fq_schema_naming=true,default_response=false:. \
api/helloworld/v1/error_reason.proto api/helloworld/v1/greeter.proto api/testdomain/v1/demo.proto
make config;
protoc --proto_path=./internal \
--proto_path=./third_party \
--go_out=paths=source_relative:./internal \
internal/conf/conf.proto
make generate;
go generate ./...
wire: demo-service/cmd/demo-service: wrote demo-service/cmd/demo-service/wire_gen.go
go mod tidy
Generate demo.pb.go
,demo_grpc.pb.go
and demo_http.pb.go
├── api
│ └── testdomain
│ └── v1
│ ├── demo.pb.go
│ ├── demo.proto
│ ├── demo_grpc.pb.go
│ └── demo_http.pb.go
Step 6: Generate a service Implementation for Demo proto
kratos proto server api/testdomain/v1/demo.proto -t internal/service
Navigate to generate service implementationinternal/service/demo.go
Update the wire
providers for the service with NewDemoService
Import the demo
API as demov1 "demo-service/api/testdomain/v1
in the internal/server/grpc.go
and internal/server/http.go
.
Add the demov1.RegisterDemoserviceServer(srv, demo) in grpc.go
and demov1.RegisterDemoserviceHTTPServer(srv, demo) in http.go
Step 7: Test the registered Demo service
Run the make all
and re-run the service with kratos run
Try the grpcurl
to list
all available methods expose at the gRPC localhost:9000
endpoint.
grpcurl -plaintext localhost:9000 list api.testdomain.v1.Demo
api.testdomain.v1.Demo.CreateDemo
api.testdomain.v1.Demo.DeleteDemo
api.testdomain.v1.Demo.GetDemo
api.testdomain.v1.Demo.ListDemo
api.testdomain.v1.Demo.UpdateDemo
We can see that the newly added Demo
service is exposed with HTTP and gRPC endpoints. We can give it a try to GetDemo
method
grpcurl -plaintext localhost:9000 api.testdomain.v1.Demo.GetDemo
{}
The response is empty because we haven’t added any logic to GetDemo
Implementation yet.
Step 8: Add a business layer for the Demo service
DemoRepo, which implements the data layer logic to persist the demo data into the SQLite database.
Step 10: Test the demo service
Run the service kratos run
Test the REST calls
curl http://127.0.0.1:8000/api/testdomain/v1/demo \
--header 'Content-Type: application/json' \
--data-raw '{
"demo": {
"name": "testssss",
"email": "adad@asd"
}
}'
# Response
{"message":"created"}%
curl http://127.0.0.1:8000/api/testdomain/v1/demo\?count\=10
# Response Output
{"demo":[{"name":"","email":""},{"name":"","email":""},{"name":"","email":""},{"name":"","email":""},{"name":"","email":""},{"name":"","email":""},{"name":"","email":""},{"name":"","email":""},{"name":"test","email":"adad@asd"},{"name":"test","email":"adad@asd"},{"name":"test","email":"adad@asd"},{"name":"testss","email":"adad@asd"},{"name":"testssss","email":"adad@asd"},{"name":"testssss","email":"adad@asd"},{"name":"testssss","email":"adad@asd"},{"name":"testssss","email":"adad@asd"}]}%
Full demo-service example:
git clone https://github.com/akoserwal/demo-service.git
Areas of Improvement for the Go-Kratos Project
- Let's work on enhancing the community Discord channel. Due to its lower activity level, this requires attention from the maintainers to ensure its proper functioning."
- Improved examples and adding more references to Complete projects can enable new developers to adapt and learn more about how to use specific features.
- Please provide information about the process and timeline for releasing new updates, as well as the frequency of these releases.
- Please establish a clearly defined process for identifying, assessing, and addressing any Common Vulnerabilities and Exposures (CVEs) that may affect the project's dependencies as well as the project itself.
- To enhance community support, the Go-Kratos team needs to aim to grow the global network of contributors and maintainers.
- Technical improvements require support for the gRPC Stream
Conclusion
The Go-Kratos framework is a powerful tool for developing REST/gRPC services in Go. It brings substantial business advantages, including scalability, flexibility, and reduced technical debt. Although there is a learning curve, its exceptional performance and feature-rich nature make it an ideal option for enterprises seeking to create resilient, scalable microservices.
By leveraging go-kratos, businesses can achieve faster development cycles, maintain high availability, and ensure their services are ready to scale as demand grows.