Finally, give up gin, echo and <your other framework>

by Edgar Sipki

Introduction: Revisiting Traditions in the World of Go
In the world of Go development, choosing a framework often turns into a loyalty comparable to choosing between Apple and Android. Frameworks like Gin, Echo, and Beego have been loyal companions for programmers for many years, helping to quickly deploy projects and achieve early successes. They offered convenient tools and familiar work patterns, but over time it becomes clear that each has its own "language". This creates difficulties when switching between them or integrating with other systems.
The situation worsens if projects grow and scalability and support become critically important. Here, every incompatible component or variety of approaches turns into an obstacle requiring additional resources and time. It's like moving into a new house every time you switch from one framework to another, where even the familiar light switch is not where your hand automatically reaches for it.
For those who want to delve into the history and features of each framework, I suggest familiarizing yourself with detailed reviews and comparisons:
In this article, we will explore why it's time to abandon the usual diversity in favor of standardization and how gRPC-Gateway can become a "universal translator" capable of unifying the disparate dialects of Go frameworks into a single, universally understandable language for microservice interaction.
Standardization, Standardization, and More Standardization
When it comes to building servers in Go, each framework offers its own set of rules and tools. Initially, this seems like a big plus: you can choose exactly what suits your specific needs and work style. However, imagine the complexity when it's necessary to combine modules written using different frameworks within one project. It's comparable to trying to assemble a puzzle where each piece is made by different manufacturers and doesn't fit with the others.
Examples of Gin, Echo, and, God forbid, Beego, often come up in developer conversations. They have undoubtedly had a huge impact on the Go community and have supported many projects at different stages of their life cycle. Especially Gin, which has been a reliable tool for many for over 10 years. But modern development realities demand flexibility and scalability, and being tied to one framework can seriously limit both of these characteristics.
For example, if your project started with Echo and you want to integrate a third-party library written for Gin, you will face the need to rewrite code or create "bridges" between the API differences. This not only increases the workload but also adds additional complexity to maintaining and updating the code.
Standardization is the key to solving these problems. Transitioning to using gRPC and HTTP/2, as well as adopting tools like gRPC-Gateway, opens up new horizons in terms of compatibility and universal communication. Think of gRPC-Gateway as a universal translator that allows you to speak one language with different systems without delving into the peculiarities of each.
Introduction to gRPC-Gateway
gRPC-Gateway is a magical bridge that connects the old world of RESTful JSON APIs and the modern world of gRPC. It allows your services to speak two languages, serving both traditional HTTP requests and powerful gRPC calls. This is especially convenient when you have diverse clients: some prefer to communicate through simple HTTP requests, while others use gRPC.
Let's consider a real-life example: you manage a web service that provides weather information. Your data is not just text; it's voluminous datasets from weather stations. Here, gRPC is ideal for transferring this data with high speed and reliability. But your clients are not just other servers; they also include mobile applications and web browsers that expect convenient and understandable JSON responses.
This is where gRPC-Gateway comes into play: it transforms HTTP requests into gRPC calls and back, making your service accessible to everyone without the need to support two different APIs. All this happens behind the scenes, and to the end user, it looks like regular interaction with a website or application.
A code example you might see without gRPC-Gateway looks something like this:
syntax = "proto3"; package your.service.v1; option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; message StringMessage { string value = 1; } service YourService { rpc Echo(StringMessage) returns (StringMessage) {} }
And now let's add a bit of "magic" with gRPC-Gateway:
syntax = "proto3"; package your.service.v1; option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; import "google/api/annotations.proto"; message StringMessage { string value = 1; } service YourService { rpc Echo(StringMessage) returns (StringMessage) { option (google.api.http) = { post: "/v1/example/echo" body: "*" }; } }
These few lines allow you to automatically accept HTTP POST requests at the /v1/example/echo address and process them as gRPC calls. This is an example of how gRPC-Gateway simplifies life for developers by providing a single, universally understandable API.
Combining GRPC and HTTP Servers
The ability to use both gRPC and HTTP in one application is like having both a sports car and an SUV at your disposal; you can enjoy speed and power when needed and reliability and versatility at other times. gRPC-Gateway allows your service to easily switch between these two "transports," providing such flexibility.
Let's consider a typical workflow:
  1. HTTP request: The client sends an HTTP request that reaches the gRPC-Gateway.
  1. Conversion to gRPC: The Gateway analyzes the request, converts it into the corresponding gRPC call, and forwards it to the gRPC server.
  1. Processing by the gRPC server: The gRPC server processes the request and sends the response back to the Gateway.
  1. Conversion to HTTP: The Gateway converts the gRPC response back into a format understandable by the HTTP client and sends it back.
This process can be represented as a diagram:
In real life, it looks like this: when a user requests information through a web interface, their request goes through the gRPC-Gateway, which translates it into a gRPC call. Then, after receiving a response from the gRPC service, the Gateway "translates" it back into an HTTP response and sends it to the user, ensuring a smooth and seamless interaction process.
At the code level, it might look like this:
package main import ( "context" "log" "net" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" echo "path/to/your/protobuf_package" ) const ( grpcServerEndpoint = "localhost:50051" httpServerEndpoint = "localhost:8080" ) type EchoService struct { echo.UnimplementedEchoServiceServer } func (s *EchoService) Echo(ctx context.Context, in *echo.EchoRequest) (*echo.EchoResponse, error) { return &echo.EchoResponse{Message: "Echo: " + in.Message}, nil } func main() { g, ctx := errgroup.WithContext(context.Background()) g.Go(func() error { return startGRPCServer(ctx) }) g.Go(func() error { return startHTTPServer(ctx) }) if err := g.Wait(); err != nil { log.Fatalf("Failed to start servers: %s", err) } } func startGRPCServer(ctx context.Context) (err error) { lis, err := net.Listen("tcp", grpcServerEndpoint) if err != nil { return err } s := grpc.NewServer() echo.RegisterEchoServiceServer(s, &EchoService{}) go func() { <-ctx.Done() s.GracefulStop() }() log.Printf("Starting gRPC server on %s", grpcServerEndpoint) return s.Serve(lis) } func startHTTPServer(ctx context.Context) (err error) { mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} err = echo.RegisterEchoServiceHandlerFromEndpoint(ctx, mux, grpcServerEndpoint, opts) if err != nil { return err } srv := &http.Server{ Addr: httpServerEndpoint, Handler: mux, } go func() { <-ctx.Done() srv.Shutdown(ctx) }() log.Printf("Starting HTTP server on %s", httpServerEndpoint) return srv.ListenAndServe() }
This simple example shows how within one application, both gRPC and HTTP servers can be provided, giving users the freedom of choice and offering developers convenient tools for managing APIs.
Advantages of Using gRPC-Gateway
gRPC-Gateway not only simplifies development but also opens up new opportunities for systems where compatibility and flexibility are required. This is especially important in modern microservice architectures, where systems must interact with each other seamlessly and efficiently.
Single Source of Truth: One Schema for GRPC and RESTful API
Using a single .proto file to describe both gRPC and RESTful APIs ensures unity and consistency of interfaces. This eliminates the need to maintain different versions of APIs for different protocols, simplifying updates and reducing the chances of errors. If you make changes to the .proto file, they are automatically reflected in both the RESTful API and gRPC.
Reducing Code Duplication and Simplifying Maintenance
Since gRPC-Gateway translates REST requests into gRPC calls, developers don't need to write separate implementations for each protocol. This frees up efforts and time for creating additional features rather than duplicating existing functionality.
Automatic Generation of Client Code and OpenAPI (Swagger) Documentation
gRPC-Gateway allows automatic generation of client libraries for various programming languages and API documentation in OpenAPI (Swagger) format. This not only speeds up the client-side development process but also ensures documentation relevance, making interaction with the API transparent and understandable for all parties.
An example of the advantage might look like this: imagine your team is implementing a new feature in the system. Previously, this would require updating several API documents and implementations in different languages. Now, changes are made to a single .proto file, and all necessary updates are generated automatically.
These benefits make gRPC-Gateway a powerful tool that can significantly simplify the development and maintenance process of microservice architectures, making them more modular, flexible, and easy to scale.
Disadvantages and Limitations of gRPC-Gateway
Despite significant advantages, gRPC-Gateway is not without its drawbacks, especially when it comes to complex use cases and integration with existing systems. Let's consider some of these limitations:
Limited Customization and Configuration
gRPC-Gateway works great "out of the box" for many standard scenarios, but its customization capabilities may be insufficient for unique requirements. For example, complex routing requirements, fine-tuning security, or specific request handling may require additional effort and sometimes writing an additional proxy layer.
Higher Learning Curve for Beginners
To effectively use gRPC-Gateway, developers need to learn not only the basics of REST and HTTP but also the specifics of working with gRPC and protobuf. This can create additional difficulties for beginners or teams accustomed to more traditional REST APIs.
Interaction with Other Tools and Libraries
Integration with some monitoring, logging, or tracing tools may be complicated, as gRPC-Gateway represents yet another layer of abstraction. Therefore, additional configuration or searching for specialized solutions compatible with gRPC may be required.
Limitations in Documentation Generation
While gRPC-Gateway can generate API documentation, it may not fully support all nuances and customizations of RESTful APIs, which can lead to less detailed or incomplete documentation.
Despite these limitations, gRPC-Gateway remains a powerful and flexible tool that can significantly simplify the development and maintenance of microservice architectures, making them more modular, scalable, and easy to integrate with other systems.
In conclusion, the choice of development tools and approaches should always be guided by the specific needs of the project and the team. By adopting standardization and universal communication protocols like gRPC and HTTP/2, and leveraging tools like gRPC-Gateway, developers can achieve greater efficiency, consistency, and flexibility in building and maintaining modern distributed systems.
Conclusion
The journey of Go frameworks has been a fascinating one, marked by innovation, diversity, and evolution. However, as we move forward, the need for standardization and interoperability becomes increasingly evident. gRPC-Gateway stands as a testament to this shift, offering a unified approach to building robust, scalable, and maintainable microservices.
By embracing gRPC-Gateway, developers can bridge the gap between different frameworks, reduce complexity, and create a more cohesive and efficient development environment. This not only benefits individual projects but also strengthens the Go ecosystem as a whole, paving the way for more seamless and innovative solutions.
As we continue to explore new horizons in software development, let us remember the importance of adaptability, collaboration, and continuous improvement. The tools and technologies we choose today will shape the future of our craft, and by making thoughtful, informed decisions, we can build a stronger, more resilient, and more vibrant technological landscape.
Thank you for joining us on this exploration of gRPC-Gateway and the future of Go development. We hope this journey has provided valuable insights and inspiration for your own projects and endeavors.