DEV Community

Umang Mundhra
Umang Mundhra

Posted on

The Ultimate Golang Framework for Microservices: GoFr

GoFr: The Ultimate Golang Framework for Microservices

Go is a multiparadigm, statically typed, and compiled programming language designed by Google. Many developers have embraced Go because of its garbage collection, memory safety, and structural typing system. Go web frameworks were created to ease Go web development processes without worrying about setups and focusing more on the functionalities of a project. While building small applications, frameworks may not be necessary, but for production-level software, they are crucial. Frameworks provide additional functionalities and services that can be used by other developers who want to add similar functionalities to their software rather than writing the full-fledged software by themselves.

Choosing the right framework for your needs can enable faster development cycles and easier maintenance down the road. In this article we will talk about GoFr, an opinionated Golang framework for accelerated microservice development. And we will discover why it is your ultimate choice when building microservices in Go!


GoFr & It's rich set of features:

What really makes a framework good or bad is the ease of development it provides for its user along with the range of features it offers so the user can purely focus on business logic implementation. GoFr has been built to help developers write fast, scalable and efficient API's. The framework offers a rich set of features that help developers in writing production grade microservices with ease. Let's explore some of these features:

1. Efficient Configuration Management

Environment variables are the best way to set configuration values for your software application as they can be defined at system-level, independently of the software. This is one of the principles of the Twelve-Factor App methodology and enables applications to be built with portability.

GoFr has some predefined environment variables for various purposes like changing log levels, connecting to databases, setting application name and version, setting http ports etc. The user just needs to set these in an .env file inside the configs directory of the application and GoFr automatically reads the values from that.

Here is the full list of environment variables supported by GoFr

2. Seamless Database Interactions

Managing database connections and interactions can become hectic, especially when working with multiple databases. GoFr handles database connections seamlessly using configuration variables. Not only does it manage the connections, but it also provides direct access to database objects using the GoFr context within handlers. This approach simplifies working with multiple databases. GoFr currently supports all SQL Dialects, Redis, MongoDB, Cassandra and ClickHouse databases.

Example of using MySQL and Redis DB inside the handler.

func DBHandler(c *gofr.Context) (interface{}, error) {
 var value int

// querying a SQL db
err := c.SQL.QueryRowContext(c, "select 2+2").Scan(&value)
 if err != nil {
  return nil, datasource.ErrorDB{Err: err, Message: "error from sql db"}
 }

// retrieving value from Redis
 _, err = c.Redis.Get(c, "test").Result()
 if err != nil && !errors.Is(err, redis.Nil) {
  return nil, datasource.ErrorDB{Err: err, Message: "error from redis db"}
 }

 return value, nil
}
Enter fullscreen mode Exit fullscreen mode

3. Implementing Publisher-Subscriber architecture with ease:

GoFr simplifies Pub/Sub by offering built-in support for popular clients like Kafka, Google Pub/Sub, and MQTT. This eliminates the need for manual configuration or library management, allowing you to focus on your event-driven architecture. Publishing and subscribing to events are streamlined using the GoFr context. Publishing events can be done inside the handler using the context, and to subscribe to an event, you just need to use GoFr's Subscribe handler. This approach promotes clean code and reduces boilerplate compared to implementing the Pub/Sub pattern from scratch.

Example of using Publisher and Subscriber in a GoFr application:

package main

import (
 "encoding/json"

 "gofr.dev/pkg/gofr"
)

func main() {
 app := gofr.New()

 app.POST("/publish-product", product)

// subscribing to products topic
 app.Subscribe("products", func(c *gofr.Context) error {
  var productInfo struct {
   ProductId string `json:"productId"`
   Price     string `json:"price"`
  }

  err := c.Bind(&productInfo)
  if err != nil {
   c.Logger.Error(err)

   return nil
  }

  c.Logger.Info("Received product ", productInfo)

  return nil
 })

 app.Run()
}

func product(ctx *gofr.Context) (interface{}, error) {
 type productInfo struct {
  ProductId string `json:"productId"`
  Price     string `json:"price"`
 }

 var data productInfo

// binding the request data to productInfo struct
 err := ctx.Bind(&data)
 if err != nil {
  return nil, err
 }

 msg, _ := json.Marshal(data)

// publishing message to producst topic using gofr context
 err = ctx.GetPublisher().Publish(ctx, "products", msg)
 if err != nil {
  return nil, err
 }

 return "Published", nil
}
Enter fullscreen mode Exit fullscreen mode

4. Out of the Box Observability:

Effective monitoring is crucial for maintaining high-performing microservices. GoFr takes the burden off your shoulders by providing built-in observability features. This eliminates the need for manual configuration of tracing, metrics, and logging libraries.

  • Detailed Logging: GoFr offers structured logging with various log levels (INFO, DEBUG, WARN, ERROR, FATAL) to capture application events at different granularities. This empowers you to analyze application flow, identify potential issues, and streamline debugging.

  • Actionable Metrics: GoFr automatically collects and exposes application metrics, allowing you to monitor key performance indicators. With metrics readily available, you can quickly identify bottlenecks and optimize application performance.

  • Distributed Tracing: GoFr integrates with popular tracing backends like Zipkin and Jaeger. Distributed tracing allows you to visualize the entire request lifecycle across your microservices, making it easier to pinpoint the root cause of issues within complex systems.

These observability features help users gain detailed insights into the application's flow and performance, identify and resolve bottlenecks, and ensure smooth operation.

5. Effortless Interservice HTTP Communication:

In a microservices architecture, efficient and reliable communication between services is crucial. GoFr simplifies this process by providing a dedicated mechanism to initialize and manage interservice HTTP communication. You can easily register downstream services at the application level using the AddHTTPService method.

Configurational Options for HTTP Services:

GoFr offers a variety of configuration options to enhance interservice communication:

  • Authentication: Supports APIKeyConfig, BasicAuthConfig, and OAuthConfig for secure authentication.

  • Default Headers: Allows setting default headers for all downstream HTTP service requests.

  • Circuit Breaker: Enhance service resilience with built-in circuit breaker functionality. GoFr allows you to configure thresholds and intervals to gracefully handle failures and prevent cascading outages.

  • Health Checks: Proactively monitor the health of your downstream services using GoFr's health check configuration. Define a health endpoint for each service, and GoFr will automatically verify their availability, allowing for early detection of potential issues.

These features ensure that interservice communication is secure, reliable, and easily manageable.

Example of connecting to a HTTP Service and sending a GET request:

func main() {
 a := gofr.New()

 a.AddHTTPService("cat-facts", "https://catfact.ninja",
  &service.CircuitBreakerConfig{
   Threshold: 4,
   Interval:  1 * time.Second,
  },
  &service.HealthConfig{
   HealthEndpoint: "breeds",
  },
 )

a.GET("/fact", Handler)

 a.Run()
}

func Handler(c *gofr.Context) (any, error) {
 var data = struct {
  Fact   string `json:"fact"`
  Length int    `json:"length"`
 }{}

 var catFacts = c.GetHTTPService("cat-facts")

 resp, err := catFacts.Get(c, "fact", map[string]interface{}{
  "max_length": 20,
 })
 if err != nil {
  return nil, err
 }

 b, _ := io.ReadAll(resp.Body)
 err = json.Unmarshal(b, &data)
 if err != nil {
  return nil, err
 }

 return data, nil
}
Enter fullscreen mode Exit fullscreen mode

6. Flexible Middleware Support for Enhanced Control:

Middleware allows you intercepting and manipulating HTTP requests and responses flowing through your application's router. Middlewares can perform tasks such as authentication, authorization, caching etc. before or after the request reaches your application's handler.

GoFr empowers developers with middleware support, allowing for request/response manipulation and custom logic injection. This provides a powerful mechanism to implement cross-cutting concerns like authentication, authorization, and caching in a modular and reusable way. Middleware functions are registered using the UseMiddleware method on your GoFr application instance.

Additionally, GoFr includes built-in CORS (Cross-Origin Resource Sharing) middleware to handle CORS-related headers.

Example of adding a custom middleware to GoFr application:

import (
    "net/http"

    gofrHTTP "gofr.dev/pkg/gofr/http"
)

// Define your custom middleware function
func customMiddleware() gofrHTTP.Middleware {
    return func(inner http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Your custom logic here
            // For example, logging, authentication, etc.

            // Call the next handler in the chain
            inner.ServeHTTP(w, r)
        })
    }
}

func main() {
    // Create a new instance of your GoFr application
    app := gofr.New()

    // Add your custom middleware to the application
    app.UseMiddleware(customMiddleware())

    // Define your application routes and handlers
    // ...

    // Run your GoFr application
    app.Run()
}
Enter fullscreen mode Exit fullscreen mode

7. Integrated Authentication Mechanisms:

Securing your microservices with robust authentication is crucial. GoFr streamlines this process by providing built-in support for various industry-standard authentication mechanisms. This empowers you to choose the approach that best suits your application's needs without writing complex authentication logic from scratch.

  • Basic Auth: Basic auth is the simplest way to authenticate your APIs. It's built on HTTP protocol authentication scheme. It involves sending the prefix Basic trailed by the Base64-encoded <username>:<password> within the standard Authorization header. GoFr offers two ways to implement basic authentication i.e. using pre-defined credentials as well as defining a custom validation function.

  • API Keys Auth: API Key Authentication is an HTTP authentication scheme where a unique API key is included in the request header for validation against a store of authorized keys. GoFr offers two ways to implement API Keys authentication i.e. Framework Default Validation as well as defining a Custom Validation Function.

  • OAuth 2.0: OAuth 2.0 is the industry-standard protocol for authorization. It focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices. It involves sending the prefix Bearer trailed by the encoded token within the standard Authorization header. GoFr supports authenticating tokens encoded by algorithm RS256/384/512.

Refer to the GoFr's Authentication Documentation to see the examples of how to use these auth mechanisms and know more about it.

---

  1. Automatic Swagger UI Rendering:

Providing clear and interactive API documentation is essential for user adoption and efficient development workflows. API specifications can be written in YAML or JSON. The format is easy to learn and readable to both humans and machines. The complete OpenAPI Specification can be found on the official Swagger website.

GoFr supports automatic rendering of OpenAPI (also known as Swagger) documentation. This feature allows you to easily provide interactive API documentation for your users. To allow GoFr to render your OpenAPI documentation, simply place your openapi.json file inside the static directory of your project. GoFr will automatically render the Swagger documentation at the /.well-known/swagger endpoint.


Conclusion

Throughout this article, we've explored the rich features of GoFr, an opinionated Golang framework specifically designed to accelerate microservice development. We've seen how GoFr simplifies common tasks like configuration management, database interactions, Pub/Sub integration, automatic observability, interservice communication, middleware usage, and authentication. Additionally, GoFr offers built-in support for data migrations, web sockets, cron jobs, and remote log level changes, further streamlining your development process.

We benchmarked GoFr against other popular Go frameworks such as Gin, Chi, Echo, and Fiber, and found that GoFr performed optimally, even with its extensive feature set. This means you can leverage all its powerful functionalities without compromising on performance.

We encourage you to explore GoFr for yourself. The framework's comprehensive documentation, tutorials, and active community are valuable resources to guide you on your journey. With GoFr, you can focus on building robust, scalable, and efficiently managed microservices, freeing you to dedicate more time to your application's core functionalities.

Get started with GoFr today!

Here are some helpful resources:

GoFr Website: https://gofr.dev
GoFr GitHub Repository: https://github.com/gofr-dev/gofr
GoFr Discord Server: https://discord.gg/zyJkVhps

Top comments (0)