helix is a Go library designed to simplify the development of cloud-native, consistent, reliable, and high-performance microservices. It provides a lightweight abstraction layer that streamlines complex back-end development, automating critical tasks such as logging, tracing, observability, error recording, and event propagation across services and integrations.
This allows developers to focus solely on business logic. This decoupling of concerns not only ensures that business logic remains separate from infrastructure code but also improves both developer productivity and the maintainability of large-scale systems, making the development process more modular and scalable.
At Mountaya, we rely entirely on helix for our backend
observability and reliability, showcasing its capability to support real-world,
high-performance systems. Below is an actual screenshot demonstrating its power
in action. The trace holds an event.Event object from end-to-end, across all
spans/services/integrations. It begins in our HTTP Internal API (shown in blue)
via the REST router integration and flows through to one of our Temporal workers
(shown in pink). Importantly, all observability logic is abstracted seamlessly by
helix, so developers didn't need to write code outside of the business logic —
simplifying maintenance and reducing complexity in our codebase.
-
Automatic distributed tracing and error recording: All integrations within helix automatically implement distributed tracing and error recording, adhering to OpenTelemetry standards. This ensures that developers can take full advantage of the integrations without needing to configure or modify application code. Traces and error data are captured transparently, giving you real-time visibility into your services' health and performance with zero added effort.
-
Consistent event propagation: At the heart of helix is the
event.Eventobject, which is passed seamlessly between services via distributed tracing. This object provides full end-to-end observability, enabling you to trace events across your entire service mesh with minimal setup. Whether an event is handled by one service or propagated across multiple, all its context is preserved and easily accessible for debugging, analysis, monitoring, and even usage tracking. -
Consistent error handling: helix ensures a uniform approach to error handling and recording across the core library and all integrations. By using consistent schemas and behaviors, error handling remains predictable and easy to manage across your entire stack, reducing complexity and increasing system reliability.
-
OpenAPI support: helix supports the use of OpenAPI specification to validate HTTP requests and responses against an OpenAPI description, ensuring that they match expected formats and types.
-
Type-safety everywhere: No more empty
interface{}. By leveraging Go's strong type system and generics, the library ensures type safety at every layer of the application, reducing runtime errors and improving code clarity. This means fewer bugs, easier maintenance, and a more robust developer experience.
helix relies on the following environment variables:
ENVIRONMENTrepresents the environment the service is currently running in. When value is one oflocal,localhost,dev,development, the logger handles logs atdebuglevel and higher. Otherwise, the logger handles logs atinfolevel and higher.OTEL_SDK_DISABLEDcan be set totrueto disable sending OpenTelemetry logs and traces to the OTLP endpoint. Defaults tofalse.OTEL_EXPORTER_OTLP_ENDPOINTis the OTLP gRPC endpoint to send logs and traces to. Example:localhost:4317.
This example demonstrates the full service lifecycle: initialization of the REST router integration, and the idiomatic start/stop pattern for long-running services.
import (
"net/http"
"github.com/mountayaapp/helix.go/integration/rest"
"github.com/mountayaapp/helix.go/service"
)
func main() {
// Configure and create a new REST router.
cfg := rest.Config{
Address: ":8080",
}
router, err := rest.New(cfg)
if err != nil {
return err
}
// Register a new route, including some parameters.
router.POST("/users/:id", func(rw http.ResponseWriter, req *http.Request) {
params, ok := rest.ParamsFromContext(req.Context())
if !ok {
rest.NewResponseError[rest.NoMetadata](req).
SetStatus(http.StatusInternalServerError).
Write(rw)
return
}
userID := params["id"]
// ...
// Write consistent HTTP responses using the integration's helpers.
rest.NewResponseSuccess[types.CustomMetadata, rest.NoData](req).
SetStatus(http.StatusAccepted).
SetMetadata(metadata).
Write(rw)
})
// Start the service. It handles all integrations start logic (when applicable)
// and runs until an interrupt signal.
if err := service.Start(); err != nil {
panic(err)
}
// Stop the service gracefully, handling connection closure across all
// integrations.
if err := service.Stop(); err != nil {
panic(err)
}
}Event propagation
The event.Event object seamlessly carries context (like UserId) across
service boundaries, automatically tied to the distributed trace.
import (
"github.com/mountayaapp/helix.go/event"
"github.com/mountayaapp/helix.go/integration/rest"
"github.com/mountayaapp/helix.go/integration/temporal"
)
router.POST("/anything", func(rw http.ResponseWriter, req *http.Request) {
var e = event.Event{
// ...
}
// Attach the event to a context.
ctx := event.ContextWithEvent(req.Context(), e)
// The event is automatically propagated to the Temporal integration via ctx.
wr, err := TemporalWorkflow.Execute(ctx, client, opts, payload)
if err != nil {
rest.NewResponseError[rest.NoMetadata](req).
SetStatus(http.StatusServiceUnavailable).
Write(rw)
return
}
rest.NewResponseSuccess[rest.NoMetadata, rest.NoData](req).
SetStatus(http.StatusAccepted).
Write(rw)
})Logging
Logs are automatically enriched with the trace and event.Event from the context,
ensuring immediate correlation between logs and traces across all services.
import (
"github.com/mountayaapp/helix.go/event"
"github.com/mountayaapp/helix.go/integration/rest"
"github.com/mountayaapp/helix.go/telemetry/log"
)
router.POST("/anything", func(rw http.ResponseWriter, req *http.Request) {
var e = event.Event{
// ...
}
// Attach the event to a context.
ctx := event.ContextWithEvent(req.Context(), e)
// Log message is automatically tied to the current trace and event via ctx.
log.Debug(ctx, "automatically tied to router's trace with event via ctx")
rest.NewResponseSuccess[rest.NoMetadata, rest.NoData](req).
SetStatus(http.StatusAccepted).
Write(rw)
})Custom tracing spans
In addition to all automatic traces built in integrations, you can easily start new spans that are automatically children of the current trace, allowing for fine-grained performance analysis of internal logic.
import (
"github.com/mountayaapp/helix.go/event"
"github.com/mountayaapp/helix.go/integration/rest"
"github.com/mountayaapp/helix.go/telemetry/log"
"github.com/mountayaapp/helix.go/telemetry/trace"
)
router.POST("/anything", func(rw http.ResponseWriter, req *http.Request) {
var e = event.Event{
// ...
}
// Attach the event to a context.
ctx := event.ContextWithEvent(req.Context(), e)
// Start a new span, which is a child of the current HTTP request trace.
ctx, span := trace.Start(ctx, trace.SpanKindClient, "span title")
defer span.End()
log.Debug(ctx, "log is tied to the router's trace and this custom span via ctx")
rest.NewResponseSuccess[rest.NoMetadata, rest.NoData](req).
SetStatus(http.StatusAccepted).
Write(rw)
})Error handling
Use errorstack package to build structured, traceable errors that can
accumulate validation failures and other context, improving debugging
consistency.
stack := errorstack.New("Failed to initialize Stripe client", errorstack.WithIntegration("stripe"))
stack.WithValidations(errorstack.Validation{
Message: fmt.Sprintf("%s environment variable must be set and not be empty", envvar),
})
if stack.HasValidations() {
return stack
}Each integration in helix is highly opinionated, designed to enforce modern best practices for resilience and observability right out of the box. This approach ensures developers immediately benefit from consistent error handling, structured logging, and robust distributed tracing across every external dependency.
Supported integrations:
- REST router for building REST APIs.
- Temporal for durable, fault-tolerant executions.
- PostgreSQL as transactional database.
- ClickHouse as analytical database.
- Valkey as key/value cache database.
- Bucket for standardized blob storage.
helix automatically detects the orchestrator or cloud provider your service is running on. When a recognized orchestrator or cloud provider is identified, traces and logs are automatically enriched with the corresponding attributes and fields.
Kubernetes
Additional trace attributes:
kubernetes.namespacekubernetes.pod
Additional log fields:
kubernetes_namespacekubernetes_pod
Nomad
Additional trace attributes:
nomad.datacenternomad.jobnomad.namespacenomad.regionnomad.task
Additional log fields:
nomad_datacenternomad_jobnomad_namespacenomad_regionnomad_task
Render
Additional trace attributes:
render.instance_idrender.service_idrender.service_namerender.service_type
Additional log fields:
render_instance_idrender_service_idrender_service_namerender_service_type
Repository licensed under the MIT License.