Status: ✅ Implemented
Owner: Cesar Romero & Engineering Team
Created: 2026-06-30
Dependencies: S39 (Native Server Engine), S20 (Fluent REST Evolution)
Enables: Safe, idempotent complex data retrieval with request bodies, caching of query operations, and standard-compliant resource discovery via Accept-Query.
Implement support for the standardized HTTP QUERY method (RFC 10008) across both the client-side (TRestClient / TRestRequest) and server-side (IApplicationBuilder / routing engines) components of the Dext Framework. This enables clients to perform complex read-only queries with structured request bodies (JSON, SQL, GraphQL, etc.) in a safe, idempotent, and cacheable manner, without the URL length limits of GET or the semantic misuse of POST.
Traditionally, web API developers faced a compromise when transmitting complex queries:
- GET is safe and idempotent but lacks a request body. Queries must be placed in the URL query string, risking length limits, URL encoding bottlenecks, and exposure in logs.
- POST supports a request body but is semantically unsafe and non-idempotent, preventing downstream caching (e.g., CDN and proxy caching).
RFC 10008 defines the QUERY method, which is:
- Safe & Idempotent: Semantically read-only; multiple identical requests yield identical side-effects.
- Request Body Enabled: Allows full structured request payloads.
- Cacheable: Responses can be cached based on both the URI and the request body content (via custom cache-key computation).
- Discoverable: Advertised using the
Accept-Queryresponse header to list supported media types.
- Extend the client-side Rest Client with
hmQUERYmethod enum and fluent methods. - Implement server-side routing support for the
QUERYmethod usingMapQuery. - Support the
Accept-Queryheader to declare accepted request body formats. - Integrate query caching using a request body hashing strategy.
- Guarantee high performance and zero-allocation processing of incoming request methods and headers.
We will introduce hmQUERY to the TDextHttpMethod enum and map the string "QUERY" to it.
- Modify
TDextHttpMethodenum:TDextHttpMethod = (hmGET, hmPOST, hmPUT, hmDELETE, hmPATCH, hmHEAD, hmOPTIONS, hmQUERY);
- Modify the string mapping helper to translate
hmQUERYto'QUERY'and vice-versa:hmQUERY: MethodStr := 'QUERY';
- Modify
THttpExecutor.MethodToEnumto handle the'QUERY'method string.
We will expose mapping methods for QUERY across all application builders.
- Add
MapQuerytoIApplicationBuilder:function MapQuery(const Path: string; Handler: TStaticHandler): IApplicationBuilder;
- Implement
MapQueryonTApplicationBuilder:function TApplicationBuilder.MapQuery(const Path: string; Handler: TStaticHandler): IApplicationBuilder; begin Result := MapEndpoint('QUERY', Path, Handler); end;
- Extend
THttpAppBuilderHelperhelper with fluent overloads forMapQueryand generic variations (MapQuery<T>,MapQuery<T, TResult>, etc.).
- Implement
TApplicationBuilderExtensions.MapQuerystatic variations.
- Implement
MapQuery<T>inTApplicationBuilderWithModelBinding.
To declare which media types are supported for QUERY requests at a given endpoint, Dext will provide a metadata attribute and fluent configuration to automatically append the Accept-Query header on options requests:
App.MapQuery('/search', SearchHandler)
.AcceptsQuery('application/jsonpath', 'application/sql');For endpoints mapped with MapQuery, an implicit OPTIONS handler response will automatically include QUERY in the Allow header and the registered query content-types in the Accept-Query header.
Because QUERY is safe and idempotent, responses are cacheable. However, unlike GET requests where the URL is the unique cache key, the cache key for QUERY must be constructed using both the Request URI and the hash of the Request Body.
- When a query response is marked cacheable (e.g. via
Response.Headers.Add('Cache-Control', ...)), the caching middleware must calculate a hash of the request body. - The cache key will be generated using a zero-allocation hashing mechanism (e.g., xxHash or MurmurHash3) over the request body bytes.
- Key format:
dext:cache:query:[MethodHash]:[URIHash]:[BodyHash].
To maintain consistency with the Dext codebase, the following coding guidelines must be strictly enforced:
- No inline variables: Declare variables inside the
varblock of the function/procedure, never inline. - No local variable prefixes: Do not prefix local variables with the
Lprefix (e.g., usebodyStreaminstead ofLBodyStream). - Loop counter: If a loop counter variable is named
I, it must be written in lowercasei(e.g.,for i := 0 to count - 1 do).
- Use
TSpan<Char>andTSpan<Byte>: For parsing request headers, method strings, and hashing request body streams, prefer raw stack-allocated memory orTSpanslices. - String comparison bottlenecks: Use case-insensitive methods operating directly on spans to parse the request method. Avoid constructing intermediate strings.
- Dext Collections: Never use
System.Generics.Collectionsclasses (likeTList<T>orTDictionary<K,V>). Always utilize optimized customDext Collections(such asTDextList<T>or custom maps).
- Create a new unit test suite:
WebFrameworkTests.Tests.QueryMethod.pasin the Web.FrameworkTests project using the native Dext testing library. - Assert that
QUERYrequests can be sent viaTRestClientand successfully routed byTApplicationBuilder. - Assert that the
Accept-Queryheader is present when querying usingOPTIONS. - Test that model binding successfully extracts data from the body of a
QUERYrequest. - Assert that the Cache middleware constructs correct keys for identical query URIs but different request bodies.
- Run the benchmark suite to ensure that introducing the
QUERYmethod parser insideTHttpExecutordoes not introduce any performance regression for existingGET/POSTrequests.