@danmat/query-server
v0.1.2
Published
Framework-agnostic server helpers for the HTTP QUERY method (RFC 10008): validate requests, negotiate content types, and advertise Accept-Query. Built on Web-standard Request/Response.
Maintainers
Readme
@danmat/query-server
Framework-agnostic server helpers for the HTTP QUERY method (RFC 10008). Validate incoming QUERY requests, enforce the RFC's Content-Type rule, negotiate accepted query formats, and advertise them with Accept-Query.
Built on Web-standard Request/Response, so it runs anywhere they do — Hono, Deno, Bun, Cloudflare Workers, and Node (via a web adapter). Its only dependency is @danmat/accept-query.
import { checkQueryRequest, readQueryJson, withAcceptQuery } from "@danmat/query-server";
const ACCEPTED = ["application/json", "application/sql"];
async function handler(request: Request): Promise<Response> {
// Reject non-QUERY, missing/unsupported Content-Type — with correct status codes.
const rejection = checkQueryRequest(request, { accept: ACCEPTED });
if (rejection) return withAcceptQuery(rejection, ACCEPTED);
const query = await readQueryJson<{ filter: unknown }>(request);
const results = await runQuery(query);
return withAcceptQuery(Response.json(results), ACCEPTED);
}Why?
RFC 10008 puts real obligations on the server: it MUST reject a QUERY whose Content-Type is missing, it should tell clients which query formats it accepts (via Accept-Query), and it needs to answer the method-override fallback that clients use when they're unsure the server speaks QUERY. This library packages those rules so your handler stays about your query logic.
Install
npm install @danmat/query-serverAPI
isQueryRequest(request, options?): boolean
Whether a request should be handled as a QUERY. Recognizes the QUERY method and, by default, POST + X-HTTP-Method-Override: QUERY (the fallback used by clients like @danmat/query-fetch). Disable with { allowMethodOverride: false }.
assertQueryRequest(request, options?): void
Throws a QueryRequestError (carrying the correct HTTP status and headers) when the request isn't a valid QUERY:
| Condition | Status | Extra |
| --- | --- | --- |
| Not a QUERY request | 405 | Allow: QUERY |
| Missing Content-Type | 400 | — |
| Content-Type not in accept | 415 | Accept-Query: … |
Pass { accept: ["application/json", …] } to enable media-type negotiation (wildcards and parameters supported).
checkQueryRequest(request, options?): Response | null
Non-throwing companion — returns a ready-to-send error Response, or null when the request is valid.
readQueryJson<T>(request): Promise<T>
Reads the body as JSON, guarding the content type (415 for a non-JSON type, 400 for malformed JSON).
acceptQueryHeader(mediaTypes): string
Builds an Accept-Query header value from the media types you accept (strings and/or structured ranges with q weights).
withAcceptQuery(response, mediaTypes): Response
Returns a copy of response with the Accept-Query header set — handy on both success and 415 responses.
QueryRequestError
Error subclass with status: number, headers: Record<string,string>, and toResponse(): Response.
The @danmat QUERY suite
@danmat/query-fetch— client for the QUERY method.@danmat/accept-query— parse/build/negotiateAccept-Query.@danmat/query-cache— body-aware response caching.@danmat/query-server— server-side request validation & negotiation (you are here).
▶️ See them work together: query-suite-example — a runnable demo using all four, with a 🌐 live playground.
License
MIT © Dan Matthew
