Building a REST/gRPC Service Using the Go-Kratos Framework

Abhishek koserwal
8 min readAug 23, 2024

--

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:

  1. 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 Greeterservice 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/v1in the internal/server/grpc.go and internal/server/http.go.

Add the demov1.RegisterDemoserviceServer(srv, demo) in grpc.goand 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 listall 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

Step 9: Add the Data layer for the Demo

First, update the Data wrapper to create a SQLite connection, create a test.db in the root directory configured from the config input.

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.

--

--

Abhishek koserwal
Abhishek koserwal

Written by Abhishek koserwal

#redhatter #opensource #developer #kubernetes #keycloak #golang #openshift #quarkus #spring

Responses (1)