http_query 0.2.0
http_query: ^0.2.0 copied to clipboard
A client and server SDK for the HTTP QUERY method (RFC 10008) — safe, idempotent requests carrying a query body.
http_query #
A client and server SDK for the HTTP QUERY method
(RFC 10008) in Dart.
QUERY is a safe and idempotent request method that carries a request
body describing a query to run against the target resource. It combines the
best of GET (safe, cacheable, retryable) and POST (arbitrary request body):
the query travels in the body instead of the URI.
Sending a query #
The async client is built on package:http.
import 'package:http_query/http_query.dart';
Future<void> main() async {
final client = QueryClient();
// Raw body + content type
final resp = await client.queryString(
'https://example.org/search',
'application/sql',
'SELECT 1',
);
// JSON in, JSON out (validates the status)
final results = await client.queryJsonInto(
'https://example.org/search',
{'q': 'dart'},
);
client.close();
}
Result vs. equivalent resource #
A QUERY response can advertise two distinct GET-retrievable URIs
(RFC 10008 §2.3–2.4):
Content-Location— the stored result of the query just run.QueryClient.fetchResultfollows it.Location— the equivalent resource, whoseGETre-runs the query.QueryClient.fetchEquivalentfollows it.
Discovering support #
Accept-Query is an RFC 9651
Structured Fields List; it is parsed and serialized correctly (quoted strings,
parameters, */* and type/* wildcards).
final discovery = await client.options('https://example.org/search');
if (discovery.supportsQuery) {
print(discovery.acceptQuery);
}
Redirects #
QUERY redirects are handled per RFC 10008 §2.5:
301/302/307/308 re-issue the QUERY with its body, while only 303
switches to GET. The client sends requests with followRedirects = false so
it can apply these rules (the dart:io default would re-issue 303 as QUERY
and drop the body on 301/302). Cap the count via the maxRedirects
constructor argument; exceeding it throws TooManyRedirectsException.
Serving QUERY #
The server helpers are framework-agnostic (they work on a method string and a
header map), so they plug into dart:io HttpServer, shelf, etc. They enforce
the RFC's rules: 400 for a missing Content-Type, 415 for an unsupported
query media type, 405 for non-QUERY methods.
final rejection = checkQueryRequest(method, headers, ['application/json']);
if (rejection != null) {
// respond with rejection.status (400 / 405 / 415)
} else {
// evaluate the query in the request body
}
Caching #
Unlike GET, a QUERY cache entry must be keyed on the request body:
final key = cacheKey(
'https://example.org/search',
mediaTypeJson,
utf8.encode('{"q":"dart"}'),
);