In OpenAPI, how do you describe an object-valued query parameter, and when is deepObject the wrong choice?
answer
- A query string is flat, objects are not
- One option loses the parameter name
- Brackets keep the name but not nesting
- The escape hatch is a media type
- Check the server's parser first
basics
~20 sOpenAPI offers three ways: style form with explode true, which flattens the object into top-level keys; style deepObject, which produces bracketed keys like filter[status]=open; and a content-typed parameter carrying the value as JSON. deepObject fails for nested objects and arrays.
solid answer
~40 sAn object in a query string has three encodings in OpenAPI 3.x. `style: form, explode: true` flattens the properties into top-level keys — `?R=100&G=200` — which loses the parameter name and collides with any other parameter of the same name. `style: deepObject, explode: true` keeps the name as a prefix: `?filter[status]=open&filter[owner]=me`. It is the readable option, but it is defined only for flat objects — nesting and arrays inside the object have no portable meaning, and tooling disagrees about them. The third option replaces `schema` with `content`, usually `application/json`, so the value is one percent-encoded JSON document. That handles arbitrary nesting and is unambiguous, at the price of an opaque URL that is awkward to hand-write and to cache-key. Choose `deepObject` for shallow filters and `content` when the structure is genuinely nested.
code
yaml · 30 linesparameters:
- name: filter
in: query
description: Flat filter - filter[status]=open&filter[assignee]=me
style: deepObject
explode: true
schema:
type: object
properties:
status:
type: string
assignee:
type: string
- name: criteria
in: query
description: Nested value carried as percent-encoded JSON
content:
application/json:
schema:
type: object
properties:
range:
type: object
properties:
from:
type: string
format: date
to:
type: string
format: datego deeper
Recall that an object cannot go into a query string without a rule, and that OpenAPI has to be told which of several bracket or comma conventions is meant.
Explain what each of the three encodings produces on the wire and why form with explode: true loses the parameter name.
Demonstrate that you verify the server framework's parser first, keep deepObject to flat objects, and reach for content: application/json or a request body once the structure nests.
Own the estate-wide convention for filter parameters so that clients generated from different services do not each guess a different bracket grammar, and weigh URL length and cacheability against expressiveness.
## Why an object in a query string is awkward A query string is a flat list of name/value pairs. Anything with structure has to be projected onto that list, and there is no single standard for doing it — Rails, PHP, Spring and Express all parse slightly different conventions. OpenAPI 3.x gives you three declarations, and the interview question is really about which one you pick and what each costs. ## Option 1 — form with explode: true This is the default for query parameters, so it is what you get if you write an object schema and no serialization keywords. Each property becomes its own top-level key. For a parameter named `color` with value `{"R": 100, "G": 200, "B": 150}`, the URL is `?R=100&G=200&B=150`. The parameter's own name disappears from the wire entirely. That has two consequences: you cannot tell which object a key belonged to, and two different object parameters sharing a property name collide irrecoverably. It is acceptable only when the object is really just a shorthand for a set of independent flat parameters — and in that case you may as well declare them as separate parameters, which documents better. ## Option 2 — form with explode: false The same object becomes `color=R,100,G,200,B,150`: keys and values interleaved in one comma list. It preserves the parameter name but is unreadable, brittle if any value contains a comma, and supported inconsistently by server frameworks. It exists for completeness; it is rarely the right pick. ## Option 3 — deepObject `style: deepObject` with `explode: true` produces bracketed keys: `?color[R]=100&color[G]=200&color[B]=150`, or in the more typical case `?filter[status]=open&filter[assignee]=me`. This reads well, keeps the parameter name, and matches what several popular frameworks already parse. Its limits matter: - It is defined **only for objects** — not arrays, not primitives. - Its behaviour for **nested** objects and for arrays inside the object is not portably defined, and generators and validators differ. If your filter has `filter[range][from]` or `filter[tags][0]`, you are outside what the specification pins down and you are betting on one particular tool's interpretation. - The brackets must be percent-encoded in a strictly conforming URL, and tools differ about whether they do so. So `deepObject` is right for a **flat** bag of scalar properties, and wrong the moment the object nests. ## Option 4 — a content-typed parameter A Parameter Object may use `content` **instead of** `schema` — the two are mutually exclusive, and the `content` map must contain exactly one entry. The usual choice is `application/json`. The client serializes the value as JSON and percent-encodes it, giving `?filter=%7B%22status%22%3A%22open%22%7D`. This is the only option that expresses arbitrary nesting without ambiguity, because the serialization is a real, specified format rather than a projection onto flat keys. The costs are real too: the URL is unreadable and unpleasant to construct by hand or in a browser address bar; it is long, and long query strings run into proxy and server URL length limits; caching keys become sensitive to JSON key ordering and whitespace; and some older tooling handles `content` on parameters poorly compared to `schema`. ## Choosing A practical rule: if the object is flat and small, use `deepObject` and keep the parameter name in the brackets so the URL stays legible. If the structure genuinely nests — a filter grammar, a range object, a list of criteria — use `content: application/json` and accept the opaque URL, or reconsider whether the operation should take a body instead. If you find yourself relying on `deepObject` with nested brackets, you have left the part of the spec that different tools agree on, and the symptom will be a generated client whose URL the server silently parses into a partly-empty object. ## What to verify before writing it down Whichever you pick, check what the **server framework actually parses** first. The OpenAPI document is a description; it does not change the parser. The failure mode is quiet: the request succeeds, the filter object arrives half-populated, and the endpoint returns the unfiltered collection.
- What happens to the parameter name when an object query parameter uses style form with explode true?It disappears from the URL. Each property becomes a top-level key, so `color` holding `{R:100,G:200}` is sent as `?R=100&G=200`. Two object parameters that share a property name then collide with no way to tell them apart, which is why this default is a poor fit for anything but a flat bag of independent values that would read better as separate parameters anyway.
- Can a parameter declare both schema and content?No — they are mutually exclusive, and a `content` map on a parameter must contain exactly one entry. `schema` plus `style`/`explode` describes a value projected onto the URL grammar; `content` describes a value serialized in a named media type. A document with both is invalid, and linters report it.
- When would you move a complex query object into a request body instead?When the structure has outgrown a URL: deep nesting, long lists, or values that push the query string toward proxy length limits. The tradeoff is that the operation stops being a cacheable, bookmarkable GET. Some teams keep GET for the simple case and add a separate search operation that takes a body for the complex one, describing both in the same document.
saying these in an interview costs you the question
- Claims deepObject supports arbitrary nesting
- Thinks the parameter name survives form with explode true
- Uses schema and content on the same parameter
- Assumes every framework parses bracketed keys the same way
- Writes the object style without checking the server parser