Skip to content

Add HTTP QUERY Method Support (RFC 10008) - #3760

Open
thzgajendra wants to merge 5014 commits into
gofr-dev:developmentfrom
thzgajendra:feat/http-query-method
Open

Add HTTP QUERY Method Support (RFC 10008)#3760
thzgajendra wants to merge 5014 commits into
gofr-dev:developmentfrom
thzgajendra:feat/http-query-method

Conversation

@thzgajendra

Copy link
Copy Markdown
Contributor

Objective / Issue

Closes #3756. Add first-class support for the HTTP QUERY method (RFC 10008) to gofr — both inbound route registration and the outbound HTTP service client.

QUERY is a safe, idempotent, cacheable method that carries the query in the request body. It bridges the gap between GET (no body, URL-length limits, query leaks into logs) and POST (has a body but is neither safe nor idempotent). It is well-suited to service-to-service search/query APIs.

What we found

  • Inbound: app had no way to register a QUERY route (GET/POST/PUT/PATCH/DELETE only).
  • Outbound: the httpClient interface and every decorator (retry, auth, custom-header, file-token, rate-limiter, circuit-breaker) had no QUERY method. The circuit-breaker doRequest switch also had no default case — an unrecognized method silently returned a nil response.
  • Go's net/http has no http.MethodQuery constant yet, but it accepts arbitrary method strings on both server and client, so there is no stdlib blocker.

How we fixed it

Inbound

  • App.QUERY(pattern, handler) registers the route (method string "QUERY"). The body is read via the existing ctx.Bind (Content-Type driven), identical to POST.

Outbound

  • Added Query / QueryWithHeaders(ctx, path, queryParams, body[, headers]) to the httpClient interface and httpService (delegates to the already method-agnostic createAndSendRequest).
  • Threaded the two methods through every decorator: retryProvider, authProvider, customHeader, fileTokenDecorator, rateLimiter.
  • Circuit breaker: added a QUERY case to doRequest plus the Query/QueryWithHeaders wrappers. Since QUERY is safe + idempotent, it is wrapped by the circuit-breaker and retry options like GET.
  • Added a default case to doRequest returning a new ErrUnsupportedMethod instead of silently returning nil.
  • Added the Query/QueryWithHeaders methods to the generated MockHTTP.

Design decisions / alternatives not taken

  • Content-Type 400 enforcement: RFC 10008 says a server MUST reject a QUERY with a missing/invalid Content-Type. gofr's existing POST handling is lenient (Bind returns nil for unknown types), so to avoid changing shared request handling we kept the same behavior and documented the requirement (validate in the handler). Auto-enforcement was considered but rejected for blast-radius reasons.
  • http.MethodQuery constant: not in Go's stdlib yet, so a package-level methodQuery = "QUERY" constant is used.

Docs / Test / Playground

  • Docs: updated docs/advanced-guide/http-communication/page.md with Query/QueryWithHeaders and a note on QUERY semantics.
  • Example: examples/http-server gains a SearchHandler on a QUERY /search route plus an integration test.

Test plan

  • go build ./...
  • go test ./pkg/gofr/... ./pkg/gofr/service/... pass
  • gofmt clean; lint clean for changed code
  • Inbound: end-to-end test registers QUERY /search, sends a QUERY with a JSON body, asserts the handler binds and echoes it.
  • Outbound: httpService.Query/QueryWithHeaders (method, path, query params, body, Content-Type default, empty body).
  • Decorators: Query covered for retry, auth, custom-header, file-token, rate-limiter.
  • Circuit breaker: QUERY success routing + default-case returns ErrUnsupportedMethod.

Risk & Rollback

Low risk — purely additive. New interface methods are implemented across all decorators and the mock; existing verbs are unchanged. The only behavioral change to existing code is the circuit-breaker default case, which converts a previously silent nil-response path into an explicit error. Rollback is a straight revert.

dependabot Bot and others added 30 commits February 24, 2026 12:57
* Fix: Goroutine leaks in SQL/Redis datasources by adding graceful shutdown

* Fix: compilation error in db_test.go due to struct changes

* Fix: Remove duplicate context import in db_test.go

* fixed tests and linters

* fix: handle silent failures in config details

* fix: add nil-check for logger in getIntConfig and refine test

* refactor: inline getIntConfig and remove helper as requested by reviewer

* style: fix wsl lint errors by adding missing whitespace

* refactor: update logging & remove redundant assignments as per review

---------

Co-authored-by: coolwednesday <divi229moc@gmail.com>
Co-authored-by: Divya Darshana <98943137+coolwednesday@users.noreply.github.com>
Co-authored-by: Umang Mundhra <mundhraumang.02@gmail.com>
* update release version to v1.50.0

* V1.50.1 (gofr-dev#2709)

* update release version to v1.50.2

* update release version to v1.51.0

* update release version to v1.52.0

* Release v1.53.0 (gofr-dev#2828)

* build(deps): bump gofr.dev in /pkg/gofr/datasource/file/gcs

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.51.0 to 1.52.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.51.0...v1.52.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.52.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump google.golang.org/api in /pkg/gofr/datasource/file/gcs

Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.258.0 to 0.259.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](googleapis/google-api-go-client@v0.258.0...v0.259.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-version: 0.259.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Metrics in HttpService Options (gofr-dev#2812)

* fixed doc formatting issue in http Communication page in documentation (gofr-dev#2827)

* build(deps): bump gofr.dev in /pkg/gofr/datasource/pubsub/sqs

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/file/sftp

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/pubsub/eventhub

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump github.com/godror/godror

Bumps [github.com/godror/godror](https://github.com/godror/godror) from 0.49.6 to 0.50.0.
- [Changelog](https://github.com/godror/godror/blob/main/CHANGELOG.md)
- [Commits](godror/godror@v0.49.6...v0.50.0)

---
updated-dependencies:
- dependency-name: github.com/godror/godror
  dependency-version: 0.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/file/s3

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump golang.org/x/term from 0.38.0 to 0.39.0

Bumps [golang.org/x/term](https://github.com/golang/term) from 0.38.0 to 0.39.0.
- [Commits](golang/term@v0.38.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-version: 0.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/dbresolver

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /examples/using-add-filestore

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump modernc.org/sqlite from 1.42.2 to 1.43.0

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.42.2 to 1.43.0.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.42.2...v1.43.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump crate-ci/typos in the actions group

Bumps the actions group with 1 update: [crate-ci/typos](https://github.com/crate-ci/typos).


Updates `crate-ci/typos` from 1.41.0 to 1.42.0
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](crate-ci/typos@v1.41.0...v1.42.0)

---
updated-dependencies:
- dependency-name: crate-ci/typos
  dependency-version: 1.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/file/gcs

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump golang.org/x/text from 0.32.0 to 0.33.0

Bumps [golang.org/x/text](https://github.com/golang/text) from 0.32.0 to 0.33.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](golang/text@v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-version: 0.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/file/azure

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/oracle

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gofr.dev in /pkg/gofr/datasource/file/ftp

Bumps [gofr.dev](https://github.com/gofr-dev/gofr) from 1.52.0 to 1.53.0.
- [Release notes](https://github.com/gofr-dev/gofr/releases)
- [Commits](gofr-dev/gofr@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: gofr.dev
  dependency-version: 1.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix/circuit breaker (gofr-dev#2829)

* feat(http): implement server-side rate limiter middleware (gofr-dev#2563)

* added version

* version

* version added

* fixed linters

* updated  version

* version-change

* feat(sqs): add span links for pub/sub tracing

Add distributed trace context propagation for AWS SQS:
- Inject W3C traceparent into SQS message attributes on publish
- Extract trace context and create span links on subscribe
- Producer/consumer spans linked via OTel span links pattern

This follows the OpenTelemetry messaging semantic conventions where
consumer spans link to producer spans rather than using parent-child.

Fixes gofr-dev#3015

* update with development and remove unwanted changes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Umang01-hash <mundhraumang.02@gmail.com>
Co-authored-by: coolwednesday <divi229moc@gmail.com>
Co-authored-by: Divya Darshana <98943137+coolwednesday@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: DuskWarden <pawar96sameer@gmail.com>
Co-authored-by: BITTU KUMAR <152794122+CSEBittuKr@users.noreply.github.com>
Co-authored-by: Bhavishya Sharma <bhavishaya32@gmail.com>
Co-authored-by: gizmo-rt <pawanrajmurarka@gmail.com>
Co-authored-by: rohansood10 <rohansood10@users.noreply.github.com>
Ujjwal-Singh-20 and others added 21 commits June 24, 2026 17:33
…6-07-02

chore(deps): consolidate minor dependency updates (2026-07-02)
…lone builds ahead of module tagging (gofr-dev#3655)

* chore(datasource): bump gofr.dev to v1.57.0 across submodules

Bumps gofr.dev v1.56.6 -> v1.57.0 in the 10 datasource submodules that
import core (dbresolver, file/azure, file/ftp, file/gcs, file/s3, file/sftp,
oracle, pubsub/eventhub, pubsub/nats, pubsub/sqs) plus the using-cloudsql
and using-add-filestore examples, ahead of tagging the datasource module
releases. go.mod/go.sum only; standalone (GOWORK=off) builds verified.

Co-Authored-By: claude-flow <ruv@ruv.net>

* test(eventhub): add mock expectations for consumer-group debug logs

Connect() emits Debugf("Using default consumer group: %s") and
Debug("Event Hub client initialization complete"), but the Connect()-calling
tests never expected them, leaving them red under gomock. Test-only.

Co-Authored-By: claude-flow <ruv@ruv.net>

---------

Co-authored-by: claude-flow <ruv@ruv.net>
…ofr-dev#3752)

Updates included:
- github.com/aws/aws-sdk-go-v2/config: 1.32.28 -> 1.32.30
- github.com/aws/aws-sdk-go-v2/service/s3: 1.105.1 -> 1.105.2
- github.com/aws/aws-sdk-go-v2/service/sqs: 1.45.0 -> 1.45.1
- github.com/dgraph-io/badger/v4: 4.9.3 -> 4.9.4
- github.com/prometheus/client_golang: 1.23.2 -> 1.24.0
- github.com/surrealdb/surrealdb.go: 1.5.1 -> 1.6.0
- cloud.google.com/go/pubsub: 1.50.2 -> 1.51.0
- google.golang.org/grpc: 1.82.0 -> 1.82.1
- golang.org/x/crypto: 0.53.0 -> 0.54.0
- modernc.org/sqlite: 1.53.0 -> 1.54.0

Closes: gofr-dev#3732, gofr-dev#3733, gofr-dev#3734, gofr-dev#3735, gofr-dev#3736, gofr-dev#3737, gofr-dev#3738, gofr-dev#3739, gofr-dev#3740, gofr-dev#3741, gofr-dev#3742, gofr-dev#3743, gofr-dev#3744, gofr-dev#3745, gofr-dev#3746, gofr-dev#3747, gofr-dev#3748, gofr-dev#3749, gofr-dev#3750, gofr-dev#3751

Co-authored-by: claude-flow <ruv@ruv.net>
Co-authored-by: Aryan Mehrotra <aryanmehrotra2000@gmail.com>
Add first-class support for the HTTP QUERY method both inbound and outbound.

Inbound: app.QUERY registers a route for the QUERY method; the body is read
via ctx.Bind like a POST.

Outbound: add Query/QueryWithHeaders to the HTTP service client and thread them
through every decorator (retry, auth, custom-header, file-token, rate-limiter,
circuit-breaker). QUERY is safe and idempotent, so it is wrapped by the
circuit-breaker and retry options like GET. The circuit-breaker doRequest switch
gains a QUERY case and a default case that returns ErrUnsupportedMethod instead
of silently returning a nil response.

Closes gofr-dev#3756
Add hermetic integration tests exercising an outbound QUERY through a real
circuit breaker against an httptest downstream: trip on repeated 503s
(ErrCircuitOpen, no downstream hit) and request-driven recovery once the
health probe reports UP. The breaker is constructed directly to avoid the
background health-check goroutine, keeping the tests deterministic.
QUERY (RFC 10008) is safe and idempotent, so QUERY routes now join read-only
handlers on the MCP tool surface. The tool schema advertises a required "body"
object argument carrying the query payload; on invocation the body is sent as
the QUERY request body (Content-Type application/json) while path params and any
extra args map to the path and query string. Write handlers remain unexposed.
The test schedules a "* * * * *" job to validate AddJob but only expected the
metric-registration calls. When the scheduler tick fired the job, job.run
emitted runtime metrics (RecordHistogram/IncrementCounter) from a background
goroutine with no matching expectation, so gomock called t.Fatalf after the
test had completed — panic: "Fail in goroutine after TestCronTab_AddJob has
completed". Allow the runtime metric emissions with AnyTimes so a late tick is
tolerated.
…cations

perf(logging,http): cut per-request allocations on the hot path
Umang01-hash
Umang01-hash previously approved these changes Jul 29, 2026

@Umang01-hash Umang01-hash left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work — clean and consistent. Ran it locally: QUERY /search returns 200 with the body bound and echoed, and metrics (method="QUERY"), logs (trace/span IDs) and traces all show up correctly. QUERY mirrors POST in every decorator, the circuit-breaker default case is a good catch, and no breaking change (the HTTP interface is sealed by its unexported method, so external code can't implement it anyway).

One thing to fix before merge: docs/advanced-guide/mcp/page.md still says only GET/HEAD/OPTIONS are exposed as agent tools and "an agent cannot mutate state." Since QUERY routes are now exposed too, please update that page to match — it's the one place the docs contradict the new behavior.

Minor (optional): the retry and rate_limiter QUERY tests only assert status 200, so they'd pass even if Query delegated to the wrong verb — the other decorators pin method=="QUERY", so it's covered overall, just noting for parity.

Approving — only the MCP docs update is worth doing before merge.

akshat-kumar-singhal and others added 2 commits July 29, 2026 16:35
…fer Stop

The merge resolution had kept only the name-specific runtime-metric
expectations for TestCronTab_AddJob; restore development's generic-matcher
expectations and defer c.Stop() alongside them so both branches' fixes are
preserved.
…eat/http-query-method

# Conflicts:
#	pkg/gofr/cron_test.go
Resolves conflicts from 189 files across: go.mod/go.sum (upstream deps),
CI/docs/examples (upstream-only, taken as-is), and the 32 feature-touching
files where both branches changed the same code.

Key merges:
- service/*.go: kept QUERY/QueryWithHeaders on all decorators + upstream
  named-constant refactors (apikey_auth, basic_auth, oauth)
- http/router.go: kept QUERY route registration + upstream trie router (gofr-dev#3759)
- gofr.go: kept App.QUERY() + upstream cron graceful-shutdown fix
- rest.go / tools.go / mcp.go: kept QUERY MCP exposure
- http_server.go: kept data-race fix + upstream logRouterChoice()
- examples/http-server: kept SearchHandler / integration test + upstream
  WaitForHTTPServer readiness fix
- circuit_breaker.go: kept ErrUnsupportedMethod default case + QUERY case
The upstream/development merge partially resolved router_test.go: it kept
the `require` import but dropped the 7 static-file test functions that
use it (Test_StaticFileServing_EndpointRoot, ...NonDirectoryDirName,
etc.). This left `require` imported and unused → lint failure and
`go vet` build failure on the whole `pkg/gofr/http` package on CI
(gofr-dev#3760 pipeline).

Our branch never modified this file, so upstream's version is the correct
one. Verified with `go vet` clean and `go test -count=1 ./pkg/gofr/...`
all packages green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Add support for the HTTP QUERY method (RFC 10008)