Parse, build, and content-negotiate the Accept-Query HTTP header (RFC 10008) — the response header a server uses to advertise which query-format media types it accepts on a QUERY request.
Zero dependencies. Fully typed. Isomorphic (Node, Deno, Bun, browsers, edge).
import { negotiateQuery } from "@danmat/accept-query";
// The server told us what it accepts; we pick the best format we can produce.
const format = negotiateQuery(response.headers.get("accept-query") ?? "", [
"application/json",
"application/sql",
]);
// → "application/sql"RFC 10008 introduces the QUERY method — a safe, idempotent request with a body. To tell clients how to shape that body, a server answers with an Accept-Query response header:
Accept-Query: application/sql;q=0.9, application/json;q=0.4, application/graphqlIt's the Accept grammar (media ranges + q weights + parameters), but for query payloads. This library gives you the three things you actually need for it: parse it, negotiate against it, and build it (for servers).
npm install @danmat/accept-queryParses a header value into media ranges, sorted by quality then specificity. Parsing is lenient — malformed entries are skipped, quoted parameter values are respected, and types/subtypes/param keys are lowercased.
parseAcceptQuery("application/sql;q=0.8, application/json");
// [
// { type: "application", subtype: "json", quality: 1, params: {} },
// { type: "application", subtype: "sql", quality: 0.8, params: {} },
// ]Given the server's Accept-Query value and the media types your client can produce (in your preference order), returns the best one to send — or null if the server accepts none of them. Wildcards (application/*, */*) and q=0 exclusions are handled per HTTP content-negotiation rules; the most specific matching range decides an offer's quality.
negotiateQuery("application/sql;q=0.9, application/json;q=0.4", [
"application/json",
"application/sql",
]);
// → "application/sql" (higher server quality wins)
negotiateQuery("application/json", ["text/csv"]);
// → null (server accepts none of ours)Builds a header value from strings and/or structured ranges — for servers advertising what they accept. Omits q when it's 1, trims trailing zeros, and quotes non-token parameter values.
formatAcceptQuery([
"application/json",
{ type: "application", subtype: "sql", quality: 0.8 },
]);
// → "application/json, application/sql;q=0.8"interface MediaRange {
type: string; // lowercased, "*" for wildcard
subtype: string; // lowercased, "*" for wildcard
quality: number; // 0–1, defaults to 1
params: Record<string, string>; // non-q params, lowercased keys
}AcceptQueryError is thrown only for programmer misuse (e.g. formatAcceptQuery on a range missing its type/subtype). Parsing never throws — it's deliberately liberal in what it accepts.
@danmat/query-fetch— client for the QUERY method.@danmat/accept-query— parse/build/negotiate theAccept-Queryheader (you are here).@danmat/query-cache— body-aware response caching.@danmat/query-server— server-side request validation & negotiation.
MIT © Dan Matthew