# New HTTP QUERY method and how to use it

_Published: 2026-07-11_

## **HTTP QUERY Method: Why We Needed It**

HTTP now has a method called `QUERY`. It is defined in [RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html), published in June 2026.

The reason for `QUERY` is simple.

For years, we wanted `GET` behavior, but with a request body. Since `GET` with a body was not a good portable contract, many APIs used `POST` for read-only searches. That worked, but it was a hack.

`QUERY` gives that pattern a proper HTTP method.

## **The Problem**

HTTP methods tell the server what the client wants to do.

```http
GET /users HTTP/1.1
Host: api.example.com

```

In this request, `GET` means read. `/users` is the resource.

That method is not only for your backend code. It also gives a signal to proxies, CDNs, API gateways, logs, SDKs, and monitoring tools.

Common method intent looks like this:

| **Method** | **Usual meaning** |
| --- | --- |
| `GET` | Read data |
| `POST` | Submit data, often to create something |
| `PUT` | Replace data |
| `PATCH` | Update part of data |
| `DELETE` | Delete data |
| `QUERY` | Run a read-only query with request content |

The important word is intent. HTTP methods are a shared language between the client, the server, and the systems between them.

## **GET Is Good For Simple Reads**

For small filters, `GET` is still the right choice.

```http
GET /users?search=Hari&limit=10 HTTP/1.1
Host: api.example.com

```

This is easy to understand.

The request is:

1. safe, because it is not asking the server to change data
2. idempotent, because repeating it has the same intended effect
3. cacheable, when the response headers allow caching

A cache can use the method and URL as part of the cache key.

```txt
GET + /users?search=Hari&limit=10

```

So this request can be cached separately from:

```http
GET /users?search=Rahul&limit=10 HTTP/1.1

```

For normal filters, this is simple and correct.

## **Where GET Becomes Painful**

Production filters can become large.

Example:

```json
{
  "search": "Hari",
  "filters": {
    "gender": "male",
    "age": { "lt": 30 },
    "email": { "contains": "@gmail.com" },
    "roles": ["admin", "editor"]
  },
  "sort": [
    { "field": "createdAt", "direction": "desc" }
  ],
  "include": ["profile", "permissions", "teams"],
  "limit": 50
}

```

Putting this into a URL becomes ugly.

```http
GET /users?search=Hari&filters%5Bgender%5D=male&filters%5Bage%5D%5Blt%5D=30&filters%5Bemail%5D%5Bcontains%5D=%40gmail.com...

```

This creates real issues:

1. URLs have practical size limits across clients, proxies, and servers.
2. Nested filters need encoding.
3. URLs often show up in logs, browser history, analytics tools, and error reports.
4. Complex objects do not fit naturally into query parameters.

So `GET` has the right meaning, but the URL is the wrong place for large query input.

## **Why Not Use GET With A Body**

This looks tempting:

```http
GET /users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "search": "Hari",
  "limit": 50
}

```

The problem is that request content on `GET` does not have a clear meaning in HTTP. Some tools may pass it. Some may ignore it. Some may block it. Some caches may not include it in the cache key.

So `GET` with a body is not a good contract for APIs that pass through real infrastructure.

## **The Old Hack: POST For Search**

Most teams solved this by using `POST`.

```http
POST /users/search HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "search": "Hari",
  "filters": {
    "age": { "lt": 30 },
    "email": { "contains": "@gmail.com" }
  },
  "limit": 50
}

```

This is easy for the backend.

```js
app.post("/users/search", async (req, res) => {
  const users = await db.users.findMany(req.body);
  res.json(users);
});

```

The body can contain JSON. The query can be large. The code is simple.

But the HTTP method is wrong for the job.

The client is not creating a user. It is not submitting a form. It is not starting a workflow. It is reading data.

The method says `POST`, but the intent is `GET`.

That mismatch matters.

A CDN or proxy cannot assume that `POST /users/search` is safe. A retry layer has to be careful. Logs and API docs also become less clear. You can still build around it, but now your API is carrying a workaround as part of its design.

This is the core point:

We wanted read behavior, but we used a write-shaped method because it allowed a body.

## **What QUERY Does**

`QUERY` fixes the signal.

```http
QUERY /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json

{
  "search": "Hari",
  "filters": {
    "age": { "lt": 30 },
    "email": { "contains": "@gmail.com" }
  },
  "limit": 50
}

```

Read it like this:

Run a read-only query on `/users`. The query input is in the request body.

That is what many `POST /search` endpoints were already trying to say.

The difference is that `QUERY` says it directly.

## **GET vs POST vs QUERY**

| **Property** | `GET` | `POST`** used for search** | `QUERY` |
| --- | --- | --- | --- |
| Read intent | Clear | Not clear from method | Clear |
| Query input | URL | Body | Body |
| Safe by method meaning | Yes | No | Yes |
| Idempotent by method meaning | Yes | No | Yes |
| Good for complex filters | No | Yes | Yes |
| Cache story | Simple URL key | Custom work | Body-aware key |
| Semantic fit | Good for small reads | Poor | Good |

Use `GET` for this:

```http
GET /users?limit=10 HTTP/1.1

```

Use `QUERY` for this:

```http
QUERY /users HTTP/1.1
Content-Type: application/json

{
  "filters": {
    "department": "engineering",
    "skills": ["go", "kubernetes", "postgres"],
    "experience": { "gte": 3 }
  },
  "sort": [
    { "field": "lastActiveAt", "direction": "desc" }
  ]
}

```

Use `POST` when you are creating or submitting something:

```http
POST /users HTTP/1.1
Content-Type: application/json

{
  "name": "Hari",
  "email": "hari@example.com"
}

```

Each method now has a clearer job.

## **Why Caching Gets Better**

Caching is not only about speed. It also protects the database from repeated expensive reads.

With `GET`, the URL can be part of the cache key.

```txt
GET + /users?search=Hari&limit=10

```

With `QUERY`, the body must also be part of the cache key.

```txt
QUERY + /users + hash(request body) + Content-Type

```

If two `QUERY /users` requests have the same body, they can map to the same cached response. If the body is different, the cache must treat it as a different query.

That is the missing piece.

`POST` can be handled with custom caching rules, but it does not give the same read-only signal by default. `QUERY` does.

## **Request Body Is Useful For Real Queries**

A real search may need:

1. nested filters
2. sort rules
3. pagination
4. field selection
5. joins or includes
6. query operators
7. a domain-specific query format

Those inputs fit better in a body than in a URL.

`QUERY` also does not force JSON. The body format comes from `Content-Type`.

JSON example:

```http
QUERY /contacts HTTP/1.1
Content-Type: application/json

{
  "select": ["surname", "givenName", "email"],
  "limit": 10,
  "match": {
    "email": "*@example.*"
  }
}

```

SQL-like example:

```http
QUERY /contacts HTTP/1.1
Content-Type: application/sql

SELECT surname, givenName, email
FROM contacts
WHERE email LIKE '%@example.%'
FETCH FIRST 10 ROWS ONLY

```

The server decides which formats it supports.

## **When To Use QUERY**

Use `QUERY` when all of this is true:

1. The operation is read-only.
2. The same request can be repeated safely.
3. The input is too large or too structured for a URL.
4. You want HTTP infrastructure to understand the read intent.
5. You may want caching, retries, or conditional requests.

Do not use `QUERY` for writes.

If the request creates, updates, deletes, sends an email, starts a payment, or triggers a workflow, use another method. Usually that means `POST`, `PUT`, `PATCH`, or `DELETE`.

## **Current Practical Note**

`QUERY` is new. Before using it in production, check your stack.

Ask these questions:

1. Does the client library allow the `QUERY` method?
2. Does the API gateway pass it?
3. Does the CDN understand its caching rules?
4. Does the backend framework expose the body?
5. Do logs, metrics, and tracing tools show it correctly?
6. Do security rules allow only the query formats you trust?

Also, moving data from the URL to the body does not make it secret. Your logging stack may still capture request bodies. Treat sensitive search input carefully.

## **Final Thought**

The old workaround was practical:

```txt
POST /users/search

```

But the meaning was not clean.

The client wanted to read data. The method looked like a write.

`QUERY` makes the intent match the operation:

```txt
QUERY /users

```

It says:

I am asking a complex read-only question. The question is in the body. You can reason about it like a safe and idempotent HTTP request.

That is why `QUERY` matters.

## **References**

1. [RFC 10008: The HTTP QUERY Method](https://www.rfc-editor.org/rfc/rfc10008.html)
2. [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)