← Docs

Parameter serialization

How the generated TypeScript client serializes query and path parameters per OpenAPI style, maps date/date-time to Date, and puts RFC 3339 on the wire.

Every generated parameter class serializes its path and query parameters according to the style (and explode) your OpenAPI document declares — simple and label and matrix for path parameters, form and spaceDelimited and pipeDelimited and deepObject for query parameters. This page covers the parts of that behavior that are easy to get wrong from the outside: how a format: date/date-time property is typed and rendered, what actually goes on the wire for each shape, and what a renamed property's wire key is.

Date and date-time map to Date

A schema property with format: date or format: date-time generates as a TypeScript Date, not a string. This applies everywhere the property appears — a scalar parameter, an array item, and a declared property of a deepObject or resolvable form-style object parameter's nested object:

new ListItemsParameters({
  fromTime: new Date('2026-08-13T10:20:30.000Z'), // format: date-time
  onDate: new Date('2026-08-13T00:00:00.000Z'),    // format: date
})

format: time is deliberately excluded from this mapping: a time-only string (10:20:30) is not a valid Date constructor argument, so it stays a plain string.

Nullable dates map to Date, not string

A nullable (or JSON Schema type: ['string', 'null']) format: date/date-time property still generates as Date, typed Date | null, for every shape above — a scalar parameter, an array (nullable or not), and a declared property nested in a deepObject or form-style object parameter:

new ListItemsParameters({
  expiresAt: new Date('2026-08-13T10:20:30.000Z'), // format: date-time, nullable
  notifyDates: null,                               // format: date, array, nullable
})

Serializing converts only a real Date instance to RFC 3339; a null value follows the same omit-from-the-query-string rule as undefined. Deserializing parses the wire value the same way as the non-nullable case below, assigning a real Date on success and leaving the property untouched otherwise.

Wire formats are RFC 3339

Every Date value is rendered as RFC 3339 before it is URL-encoded, never as Date's own toString() output (Wed Aug 13 2026 ..., which no server parsing RFC 3339 accepts):

  • format: date-time → the full timestamp, value.toISOString() (2026-08-13T10:20:30.000Z).
  • format: date → the date-only portion, value.toISOString().split('T')[0] (2026-08-13).

This applies uniformly to a scalar parameter, each item of an array-typed parameter (range_dates=2026-01-01,2026-01-31 for a non-exploded array of format: date), and each declared or dynamic entry of a deepObject or resolvable form-style object parameter (below). Deserializing reverses it with new Date(decoded), guarded by isNaN(dateValue.getTime()): a value that fails to parse leaves the property untouched rather than assigning an Invalid Date.

deepObject parameters serialize per key

A type: object query parameter with style: deepObject generates a nested model class with its own accessors, and serializes one query entry per declared property — reading through the accessor, not by enumerating the instance:

const url = new ListReportsParameters({
  filter: new Filter({
    since: new Date('2026-08-13T00:00:00.000Z'),
    until: new Date('2026-08-13T10:20:30.000Z'),
  }),
}).serializeUrl('/reports')
// filter[since]=2026-08-13, filter[until]=2026-08-13T10:20:30.000Z, once
// the query string is percent-decoded (each value is encodeURIComponent-ed
// before URLSearchParams itself percent-encodes the whole query string).

An undefined or null declared property is omitted from the query string entirely, rather than serializing as an empty or literal "undefined" entry.

When the parameter's schema allows additional properties (additionalProperties: { type: string, format: date }), the model carries a dynamic-entry member typed Map<string, Date> (or Map<string, string> for a non-date additionalProperties schema), and every entry in that map serializes with the additionalProperties schema's own format:

new Filter({ additionalProperties: new Map([['region', 'eu']]) })

Deserializing mirrors the same split: a bracketed key that matches a declared property name converts through that property's own format (Date, when it is one); every other bracketed key goes into the additionalProperties map.

Nested objects or arrays inside a deepObject value are not supported — OpenAPI itself defines no wire format for them, so a property typed that way keeps the previous, best-effort String(val) rendering rather than gaining one.

form-style object parameters serialize per key

A type: object query parameter with style: form (the default style for query parameters) serializes the same way once its schema resolves to a single object model — reading through each declared property's accessor, never by enumerating the instance. explode changes only the wire shape, not which properties are read:

new ListItemsParameters({
  filter: new Filter({
    since: new Date('2026-08-13T00:00:00.000Z'),
    until: new Date('2026-08-13T10:20:30.000Z'),
  }),
}).serializeUrl('/items')
  • explode: true (the query default) puts each declared, present property on its own top-level query parameter, keyed by the original schema property name: since=2026-08-13&until=2026-08-13T10%3A20%3A30.000Z.
  • explode: false joins present declared properties, plus dynamic entries, into the single comma-separated value the form style defines for an object: filter=since,2026-08-13,until,2026-08-13T10%3A20%3A30.000Z. A value containing a literal comma is encodeURIComponent-escaped before joining, so it survives the round trip; deserializing splits the raw value on its structural commas before decoding each piece, the reverse order of a decode-then-split approach, which would also split apart an escaped comma that was part of an entry's own value.

An undefined or null declared property is omitted, in both explode modes, rather than serializing as an empty or literal "undefined" entry.

When the parameter's schema allows additional properties, dynamic entries serialize the same way deepObject's do — from the model's additionalProperties map, with that schema's own date format — in both explode modes. Deserializing them back is asymmetric between the two: explode: false puts unmatched keys from the comma list into the additionalProperties map, the same as deepObject; explode: true does not read dynamic keys back at all, only the declared ones, because an unmatched top-level query parameter next to a form's other parameters is ambiguous.

Nested objects or arrays inside a form-style object value are not supported, for the same reason as deepObject. A schema with no single resolvable object model (a oneOf/union, for instance) keeps the generic Object.entries emission instead, unchanged.

Wire keys use the original schema name

A generated property name is the constrained (and, when a raw JSON Schema key would collide or is not a valid identifier, renamed — see Model naming) TypeScript identifier, but the wire key — what actually appears in the query string or path — is always the original schema property name:

// schema property "on_Date" renders as the accessor `onDate`
new Filter({ onDate: new Date('2026-08-13T00:00:00.000Z') })
  // -> filter[on_Date]=2026-08-13, not filter[onDate]=...

This applies to every style, not only deepObject: a path or query parameter whose own name Modelina had to rename still serializes and deserializes against its original schema name on the wire.

Header dates follow the same rule

A header with format: date or format: date-time (nullable or not) is typed Date, and follows the same RFC 3339 rule as a query or path parameter:

serializeGetItemHeadersHeaders({ requestedAt: new Date('2026-08-13T10:20:30.000Z') })
// -> { 'X-Requested-At': '2026-08-13T10:20:30.000Z' }

deserializeGetItemHeadersHeaders({ 'x-requested-at': '2026-08-13T10:20:30.000Z' })
// -> { requestedAt: Date }

A null header value falls back to String(...) on serialization, the same as any other nullable header kind. Deserializing an invalid or missing date string leaves the property untouched — the same rule every date parameter above follows — rather than assigning an Invalid Date. Every other header kind (number, boolean, array, plain string) is unaffected.