UNPKG

h3

Version:

Minimal H(TTP) framework built for high performance and portability.

77 lines (52 loc) 3.35 kB
# HTTP `QUERY` Method > Accept safe, cacheable requests that carry a query in the body. The [HTTP `QUERY` method (RFC 10008)](https://www.rfc-editor.org/rfc/rfc10008) is like `GET` — **safe, idempotent, and cacheable** — but carries a query in the request **body** with a `Content-Type`. It's the standard answer to "I need a GET, but my query is too large or too structured for the URL". H3 supports `QUERY` as a first-class method via [`app.query()`](/guide/basics/routing#http-query-method), plus two helper utilities. ## Register a `QUERY` Handler Read the request body just like you would for a `POST`: ```ts import { readBody } from "h3"; app.query("/books", async (event) => { const query = await readBody(event, { type: "text" }); return runSearch(query); }); ``` Because `QUERY` carries an attacker-controllable body, [body-size limits](/utils/request#assertbodysizeevent-limit) apply just like `POST`. ## Advertise Accepted Formats Use [`appendAcceptQuery`](/utils/request#appendacceptqueryevent-mediatypes) to tell clients which query formats a resource understands. It sets the `Accept-Query` response header (a [Structured Fields](https://www.rfc-editor.org/rfc/rfc8941) List), and can be set on a plain `GET` too so clients can discover formats before sending a `QUERY`: ```ts import { appendAcceptQuery } from "h3"; app.get("/books", (event) => { appendAcceptQuery(event, ["application/sql", "application/jsonpath"]); // Accept-Query: application/sql, application/jsonpath return "Send a QUERY request with a SQL or JSONPath body."; }); ``` ## Validate the `Content-Type` Use [`requireContentType`](/utils/request#requirecontenttypeevent-acceptedtypes) to enforce the RFC's error semantics. It returns the matched media type, or throws `400` (missing), `415` (unsupported), or `422` (malformed): ```ts import { requireContentType, readBody } from "h3"; app.query("/books", async (event) => { const type = requireContentType(event, ["application/sql", "application/jsonpath"]); const query = await readBody(event, { type: "text" }); return runQuery(type, query); }); ``` ## Offer a Cacheable `GET` Alternative A `QUERY` response is not addressable by URL, so browsers and CDNs can't cache it. RFC 10008 suggests pointing clients at an equivalent, cacheable `GET` via the `Content-Location` header. Stash the result under a stable id and let a client repeat the query with an ordinary, HTTP-cacheable `GET`: ```ts app.query("/books", async (event) => { const result = runQuery(type, query); const id = queryId(type, query); // stable hash of the query cache.set(id, result); event.res.headers.set("content-location", `/books/${id}`); return result; }); ``` ## Full Example A self-contained, runnable demo — a `/books` resource that accepts SQL-ish and JSONPath queries, validates the `Content-Type`, and advertises a cacheable `GET` alternative. It also serves a small interactive page at `/`. <read-more> See the full [`examples/query.mjs`](https://github.com/h3js/h3/tree/main/examples/query.mjs) source, or run it locally with `node examples/query.mjs`. </read-more> > [!NOTE] > Unlike `GET`, `QUERY` is **not** CORS-safelisted, so browsers send a preflight. If you pass an explicit `methods` allowlist to [`handleCors`](/utils/security#handlecorsevent-options), include `"QUERY"`.