Skip to content

Releases: gofr-dev/gofr

v1.56.7

05 Jun 11:48
a72a7f5

Choose a tag to compare

Release v1.56.7

⚡ Performance — HTTP Hot Path

Reduced per-request allocations and CPU across the pkg/gofr request lifecycle, closing most of the gap to the net/http router cluster.

Benchmark setup: Apple M4, GOMAXPROCS=4, payloads: plaintext (13 B), json (small object), param (single path param). Numbers are machine-specific and will differ on Linux/x86, in containers with CPU limits, and at higher core counts — treat as relative, not absolute.

End-to-end RPS (vs v1.56.6):

Endpoint v1.56.6 v1.56.7 Δ
plaintext 89,835 133,304 +48 %
json 89,177 118,513 +33 %
param 87,224 121,159 +39 %

Per-request profile (pprof):

Metric v1.56.6 v1.56.7 Δ
Allocations / req 132 KB 60 KB −54 %
CPU / req 33.5 µs 21 µs −37 %
p99 latency (plaintext) 3.7 ms 1.7 ms −54 %

What changed

Area Change Impact
OTel hot path When TRACE_EXPORTER is unset, installs a NeverSample() TracerProvider; otel.Tracer(...) handle cached in the middleware closure Spans keep unique TraceID/SpanID for log correlation but short-circuit at the sampler — 528 B → 144 B / req
Middleware StatusResponseWriter shared between Logging and Metrics and pooled; attribute slices cached per (route, method); time.Now() deferred past probe/GraphQL filters Fewer allocations per request
Response & routing Single Write with a Content-Length header (was 2–3 Writes); path.Clean skipped on already-canonical URLs; handler runs inline (no goroutine) when there's no request timeout and the request isn't a WebSocket upgrade Lower CPU and allocations on the response path

🛠️ Fixes

🔹 Tracing spans
HTTP semconv attributes (http.method, http.route, http.status_code) are now recorded natively on the request span, and the redundant per-route otelhttp.NewHandler wrap has been removed.

  • Eliminates the duplicate span emitted per request (2 spans/req → 1).
  • Span name corrected from the generic gofr-router to the standard <METHOD> <route-template> (e.g. GET /users/{id}), matching OTel HTTP semantic conventions.

Note on tracing defaults: the NeverSample() provider only applies when TRACE_EXPORTER is unset — i.e. when tracing isn't configured at all. If you set TRACE_EXPORTER, sampling and export behave as before. No configured setup is affected.

Full Changelog: v1.56.6...v1.56.7

v1.56.6

18 May 05:20
2958d5b

Choose a tag to compare

Release v1.56.6

🛠️ Fixes

  • Crontab Goroutine Leak on Shutdown — The cron scheduler's ticker goroutine ran an unbounded for range c.ticker.C loop with no stop signal, so it leaked on every App.Shutdown(). Added a done channel and a Stop() method guarded by sync.Once, and the goroutine now selects between the ticker and done to exit cleanly. App.Shutdown() now stops the cron scheduler before closing the container, so scheduled jobs are no longer torn down out of order. (#3292)

  • Circuit Breaker Read-Lock & Non-Blocking RecoverycircuitBreaker.isOpen() took a full write Lock() for a read-only state check, and tryCircuitRecovery() held the lock across the entire healthCheck call — blocking all other requests for the duration of the health probe. isOpen() now uses RLock(), and recovery releases the lock before running the health check, re-acquiring it only to reset the circuit (and only if it's still in the open state). (#3255, #3427)

  • Subscriber Commits Messages After Handler Panic — When a subscription handler panicked, panicRecovery swallowed the panic and left err as nil, so the message fell through to msg.Commit() and was acknowledged despite never being processed — causing silent message loss. The recovery path now sets a sentinel error and returns before the commit, so a panicked message is left uncommitted and will be redelivered. (#3424)

  • Go 1.26 — Bumped Go to 1.26 across all modules and CI workflows. (#3426)

  • Dependency Updates — Consolidated minor and patch dependency updates across the core module and datasource sub-modules. (#3423)

v1.56.5

08 May 06:57
0401294

Choose a tag to compare

Release v1.56.5

🛠 Fixes

  • gRPC Shutdown Nil Panic — The force-close fallback in grpcServer.Shutdown (invoked when the graceful context times out) didn't nil-check g.server. Apps that never called RegisterService panicked on SIGTERM during the grace period. Added the missing nil-check and a regression test. (#3368)

  • MongoDB / ArangoDB Operation Metricsdefer sendOperationStats(time.Now(), …) evaluated time.Now() at defer registration and then called time.Since(time.Now())
    internally, so every op reported ~0 µs duration. A few sites also placed the defer after an early-return, dropping metrics on failing ops. Replaced with an instrumentOp helper that captures start time synchronously and always fires its cleanup, so durations are accurate and error paths still record histograms. ArangoDB DropCollection /
    TruncateCollection now also time the actual op rather than just the getCollection() lookup. (#3102)

  • DBResolver TracingAddDBResolver previously dropped tracing silently because no matcher arm existed for it. Tracing is now wired, and a warning is emitted when a datasource implements UseTracer without a matcher entry. (#3102)

  • Duck-typed datasource wiringAddMongo, AddArangoDB, AddClickhouse, AddCassandra, AddPubSub, and the rest of the AddXxx family now accept the underlying behavior interface directly. The *Provider shims in pkg/gofr/container/datasources.go are marked Deprecated and retained for backwards compatibility — existing code continues to compile. (#3102)

v1.56.4

29 Apr 16:16
705a26f

Choose a tag to compare

Release v1.56.4

🛠️ Fixes

  • Kafka Stale Admin Connection — Kafka subscribers got permanently stuck on "kafka client not connected" after the broker closed an idle TCP connection (default connections.max.idle.ms = 10m). The admin multiConn was dialed only once during initialize() and retryConnect() only covered initial startup, so every Subscribe / Query call failed for the rest of the process lifetime. Added ensureConnected(ctx) which detects a stale admin conn at the entry of Subscribe / Query, re-dials the broker list, and swaps in a fresh multiConn while leaving the writer and per-topic readers untouched so in-flight work isn't disrupted. No public API change.

  • SwaggerUI Panic on Files Without ExtensionSwaggerUIHandler panicked with an index out of bounds error when serving filenames that had no extension, due to strings.Split + direct index access on the result. Replaced with filepath.Ext for safe extraction, and the handler now returns a defined errMissingFileExtension instead of panicking.

v1.56.3

21 Apr 12:29
f6f3026

Choose a tag to compare

Release v1.56.3

🛠️Fixes

  • Docs Build Prerender Crash — Fixed an unclosed dotenv code block in the Pub/Sub advanced guide (/docs/advanced-guide/using-publisher-subscriber) that causediNext.jsftort throw TypeError: Cannot read properties of undefined (reading 'toLowerCase') during prerendering, breaking the Dockerize step of the v1.56.2 release workflow.

v1.56.2

21 Apr 11:35
0438314

Choose a tag to compare

Release v1.56.2

🛠️Fixes

Consumer spans are now real children of the producer's trace across all Pub/Sub backends — Kafka, NATS, Google Pub/Sub, and SQS — while still preserving the existing OTel span link for fan-out semantics.

Previously, each consumer span started from a fresh root context, so an end-to-end publish/subscribe flow appeared as multiple disconnected traces in Jaeger, Tempo, andtracer.gofr.dev. Now the consumer span inherits the producer's trace ID and parent, giving you a single connected trace across all services with no extra configuration.

  • Fan-out preserved — span link is still attached for tools that surface fan-out semantics
  • Applies to: Kafka, NATS, Google Pub/Sub, SQS

v1.56.1

09 Apr 15:11
984051e

Choose a tag to compare

🛠️ Fixes

  • Redis Streams PubSub CPU Busy-Spin — When the message buffer was full, subscribeToStream looped at 100% CPU with no backoff because consumeStreamMessages returned immediately on zero capacity. Added a
    time.After backoff using the existing subscribeRetryInterval, following the same pattern used in the Subscribe connection-wait loop.

  • Redis Streams Goroutine Leak on Shutdown — The subscription context created in ensureSubscription was discarded (_, cancel := ...) and runSubscriptionLoop used a fresh context.Background(), making
    goroutines impossible to cancel. The cancelable context is now propagated through the full call chain, and Close() no longer holds the write lock while waiting for goroutines (which previously caused a
    guaranteed 5-second deadlock on every shutdown).

  • Redis Streams Reconnection FailureresubscribeAll was silently a no-op for streams — it canceled a context nobody held and never started a new goroutine. It now properly waits for old goroutines to exit
    and starts new ones with fresh contexts, preserving the consumer name across resubscribes to maintain PEL ownership and prevent message loss.

v1.56.0

07 Apr 11:57
0c0c1ce

Choose a tag to compare

Release v1.56.0

🚀 Features

🔹 CORS Multiple Origins Support

GoFr's CORS middleware now supports multiple allowed origins, enabling fine-grained access control for applications serving multiple frontends or admin panels.

  • Comma-Separated Origins: Configure multiple origins via the ACCESS_CONTROL_ALLOW_ORIGIN environment variable
  • Dynamic Origin Matching: On each request, the Origin header is matched against the allowed set and only the matched origin is returned in the response (per HTTP spec)
  • Correct Caching: Adds Vary: Origin header when a specific origin is matched, ensuring proxies and CDNs cache responses correctly
  • Fully Backward-Compatible: Single origin and wildcard (*) work exactly as before

Configuration Example

# Single origin (existing behavior)
ACCESS_CONTROL_ALLOW_ORIGIN=https://app.example.com

# Multiple origins (new)
ACCESS_CONTROL_ALLOW_ORIGIN=https://app.example.com,https://admin.example.com

🔧 Enhancements

🔹 Selective Migration Record Tracking

Migrations now only insert gofr_migrations records into databases that were actually used by the UP function, instead of all connected databases. Previously, running a migration that only touched MySQL would still insert migration records into Redis, MongoDB, Cassandra, and every other connected database — leading to phantom records, wasted I/O, and a misleading audit trail. Each datasource is now wrapped with lightweight usage-tracking before UP() executes, and only touched databases receive the migration record.

🛠️ Fixes

  • WebSocket Nil Pointer Panic — Fixed a nil pointer dereference in WriteMessageToSocket when no WebSocket connection exists in the context. It now returns ErrConnectionNotFound instead of panicking.

  • File Logger Descriptor LeakNewFileLogger() opens a file via os.OpenFile but previously had no way to close it, causing file descriptor leaks. Added a Close() method to the logger, which is now called during App.Shutdown() (HTTP/gRPC) and after cmd.Run() (CLI apps). Fatal/Fatalf also now call file.Sync() before exiting to flush buffered log data.

  • Cron Job Duration Log Format — Fixed %!s(float64=...) in cron job completion logs by keeping duration as time.Duration instead of converting to float64. Logs now show human-readable durations (e.g., 25.071286ms instead of %!s(float64=0.025071286)).

  • UpdateOne Breaking Change Reverted — Reverted the Mongo interface's UpdateOne return type from (int64, error) back to error, fixing a breaking change introduced in v1.55.0. The migration lock refresh now uses UpdateMany (which already returns the needed ModifiedCount) with a filter that guarantees at most one document matches.

  • Dependency Updates — Consolidated minor dependency updates and bumped github.com/go-jose/go-jose/v4 for security improvements.

v1.55.0

14 Mar 20:39
28fac0d

Choose a tag to compare

Release v1.55.0

🚀 Features

Native GraphQL Support

GoFr now provides first-class GraphQL support using a schema-first approach. Define your API contract in ./configs/schema.graphqls, register resolvers with app.GraphQLQuery() and app.GraphQLMutation(), and GoFr handles execution, validation, tracing, and metrics automatically.

  • Single Endpoint: All operations served at POST /graphql
  • Interactive Playground: Auto-hosted at /.well-known/graphql/ui
  • Full Observability: Per-resolver OpenTelemetry tracing, operation-level metrics (app_graphql_operations_total, app_graphql_error_total, app_graphql_request_duration), and structured logs aligned with GoFr's existing format
  • Operation Name Extraction: Automatically parses resolver names from the query AST for meaningful metric labels, even for anonymous operations
  • Grafana Dashboard: GraphQL panels added to the standard GoFr dashboard with Response Time SLA, Request/Error Count, and Latency breakdowns by operation and type

gRPC Rate Limiter

Built-in rate limiter interceptors for gRPC using a token bucket algorithm, supporting both unary and streaming RPCs.

  • Per-IP Rate Limiting: Each client IP gets its own rate limit bucket
  • Shared or Separate Budgets: Use a single store for combined limits or independent configs per call type
  • Prometheus Metrics: Track violations via app_grpc_rate_limit_exceeded_total counter
  • Returns RESOURCE_EXHAUSTED with retry-after metadata on limit exceeded

GCS Metadata Upload and Signed URL Generation

Google Cloud Storage now supports cloud-specific operations beyond the standard filesystem interface.

  • CreateWithOptions: Upload files with custom Content-Type, Content-Disposition, and arbitrary metadata
  • GenerateSignedURL: Create time-limited, pre-authenticated download URLs
  • file.AsCloud(): Safe type assertion helper to check cloud support

🔧 Enhancements

NATS OpenTelemetry Span Links

Added span link support for NATS JetStream PubSub operations, providing distributed tracing visibility into publish, subscribe, and commit operations.

gRPC Observability Improvements

Client-side errors (e.g., InvalidArgument, NotFound, ResourceExhausted) are no longer logged at ERROR level — only server-side errors are. This reduces log noise in production.

🛠️ Fixes

  • Migration Race Condition — Fixed a race condition where multiple pods starting simultaneously could execute the same migration concurrently. The migration state is now re-verified under the distributed lock before execution, preventing duplicate runs in multi-instance deployments.

  • Zipkin Deprecation WarningTRACE_EXPORTER=zipkin now logs an actionable deprecation warning with migration instructions to OTLP (Zipkin supports OTLP natively since v2.24+).

  • CI Workflow Updates — Bumped Docker GitHub Actions (setup-buildx-action, login-action) from v3 to v4 in website deployment workflows.

v1.54.6

11 Mar 06:17
0382490

Choose a tag to compare

Release v1.54.6

🛠️ Fixes

  • PubSub Migration Conflict Resolution

Resolved an issue where the PubSub Migrator, particularly with Redis Streams, incorrectly reported migration versions based on historical data on the message bus.

  • Decoupled Versioning: Made PubSub non-authoritative for migration state by removing it from the version chain. Migration tracking is now strictly confined to primary data stores that support atomicity and locking.
  • PubSub Still Available: The PubSub client remains fully accessible in the migration.Datasource object for use in migration UP functions (e.g., d.PubSub.CreateTopic()).