Skip to main content

Frequently Asked Questions (FAQ)

How to set 404 Not Found handler?

Use WithNotFound option:

h := myHandler{}
srv, err := api.NewServer(h, api.WithNotFound(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.WriteString(w, `{"error": "not found"}`)
}))
if err != nil {
panic(err)
}

See Static router section for more details about router errors.

How to set error handler?

Use WithErrorHandler option:

h := myHandler{}
srv, err := api.NewServer(h, api.WithErrorHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
var (
code = http.StatusInternalServerError
ogenErr ogenerrors.Error
)
switch {
case errors.Is(err, ht.ErrNotImplemented):
code = http.StatusNotImplemented
case errors.As(err, &ogenErr):
code = ogenErr.Code()
}
_, _ = io.WriteString(w, http.StatusText(code))
}))
if err != nil {
panic(err)
}

Also, check out the Convenient errors concept.

How to use net/http middlewares?

The Server type implements http.Handler interface, so you can use any net/http middleware with it.

type Middleware func(next http.Handler) http.Handler

func Wrap(h http.Handler, mw Middleware) http.Handler {
return mw(h)
}

// TraceMiddleware adds traces.
func TraceMiddleware(lg *zap.Logger, tp trace.TracerProvider) middleware.Middleware {
return func(next http.Handler) http.Handler {
t := tp.Tracer("http")
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
start := time.Now()
w := &writerProxy{ResponseWriter: rw}

ctx, span := t.Start(r.Context(), "HandleRequest")
defer span.End()
spanCtx := span.SpanContext()
ctx = With(ctx, lg.With(
zap.String("trace_id", spanCtx.TraceID().String()),
zap.String("span_id", spanCtx.SpanID().String()),
))

defer func() {
if r := recover(); r != nil {
lg.Error("Panic", zap.Stack("stack"))
if w.status == 0 {
w.WriteHeader(http.StatusInternalServerError)
}
span.AddEvent("Panic recovered",
trace.WithStackTrace(true),
)
span.SetStatus(codes.Error, "Panic recovered")
}
lg.Debug("Request",
zap.Duration("duration", time.Since(start)),
zap.Int("http.status", w.status),
zap.Int64("http.response.size", w.wrote),
zap.String("http.path", r.URL.String()),
zap.String("http.method", r.Method),
)
}()

next.ServeHTTP(w, r.WithContext(ctx))
})
}
}

func newServer() *http.Server {
h, err := oas.NewServer(h,
oas.WithMeterProvider(m.MeterProvider()),
oas.WithTracerProvider(m.TracerProvider()),
)
if err != nil {
return errors.Wrap(err, "oas server")
}
return &http.Server{
Handler: middleware.Wrap(h, TraceMiddleware(lg, m.TracerProvider())),
Addr: addr,
}
}

See Static router section for more details.

How to use Prometheus?

See OpenTelemetry Go SDK examples.

Also, see our autometer helper.

Example setup:

package main

import (
"context"

"example/internal/oas"

"github.com/prometheus/client_golang/prometheus"
otelprometheus "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

const appName = "my-ogen-service"

// setupMeterProvider setups Prometheus instrumentation.
func setupMeterProvider(reg prometheus.Registerer) (metric.MeterProvider, ShutdownFunc, error) {
// Create an OpenTelemetry Resource description.
//
// See https://opentelemetry.io/docs/languages/go/resources/
appResource, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(appName),
),
)
if err != nil {
return nil, nil, err
}

// Create Prometheus exporter and given registerer.
exp, err := otelprometheus.New(
otelprometheus.WithRegisterer(reg),
)
if err != nil {
return nil, nil, err
}

mp := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(appResource),
sdkmetric.WithReader(exp),
)
return mp, func(ctx context.Context) error {
// Flush collected metrics.
_ = mp.ForceFlush(ctx)
return mp.Shutdown(ctx)
}, nil
}

type ShutdownFunc = func(ctx context.Context) error

func createAPIServer(h oas.Handler) (_ *oas.Server, _ ShutdownFunc, rerr error) {
meterProvider, shutdown, err := setupMeterProvider(prometheus.DefaultRegisterer)
if err != nil {
return nil, nil, err
}
defer func() {
// Shutdown MeterProvider and release resources in case of error.
if rerr != nil {
shutdown(context.TODO())
}
}()

server, err := oas.NewServer(h, oas.WithMeterProvider(meterProvider))
if err != nil {
return nil, nil, err
}

// Returned `shutdown` function would stop metric collection.
return server, shutdown, nil
}

How to add some tags to generated structures?

Use x-oapi-codegen-extra-tags extension.

How to disable OpenTelemetry integration?

Disable ogen/otel feature via config:

.ogen.yml
generator:
features:
disable:
- "ogen/otel"