Skip to Content

Data transforms

When the shape of data on the wire differs from the shape SDK users should see, use the x-speakeasy-transform-from-api and x-speakeasy-transform-to-api extensions to bridge the gap automatically. These extensions apply jq expressions  to transform data during serialization and deserialization, so SDK users work with a clean interface while the underlying API format is preserved.

When to use data transforms

Data transforms are useful when:

  • The API returns a response shape that could be cleaner for SDK consumers, such as deeply nested structures that should be flattened
  • The API schema cannot be changed, but the SDK should present a better interface
  • Fields need to be derived from other fields, such as combining first_name and last_name into display_name

Extensions

Place these extensions on a schema in the OpenAPI spec, typically on request body or response schemas.

  • x-speakeasy-transform-from-api: Transforms data from the API response before the SDK user sees it.
  • x-speakeasy-transform-to-api: Transforms data to the API request format before sending.

Both extensions can be used on the same schema when transforms are needed in both directions.

Format

The value must be a YAML mapping with a jq key:

x-speakeasy-transform-from-api: jq: '.your_expression_here'

Examples

Copy a field

components: schemas: Resource: type: object x-speakeasy-transform-from-api: jq: '. + { backup_id: .id }' properties: id: type: string

Rename a field

components: schemas: Resource: type: object x-speakeasy-transform-to-api: jq: '{ displayName: .name }' properties: name: type: string

Add a derived field

components: schemas: User: type: object x-speakeasy-transform-from-api: jq: '. + { display_name: .first_name + " " + .last_name }' properties: first_name: type: string last_name: type: string

Flatten a nested structure

components: schemas: Order: type: object x-speakeasy-transform-from-api: jq: '. + { status: .metadata.status } | del(.metadata)' properties: id: type: string metadata: type: object properties: status: type: string

Conditional field addition

components: schemas: Account: type: object x-speakeasy-transform-from-api: jq: 'if .status == "active" then . + { is_active: true } else . end' properties: status: type: string

Remove a field before sending

components: schemas: UpdateRequest: type: object x-speakeasy-transform-to-api: jq: 'del(.internal_field)' properties: name: type: string internal_field: type: string

Bidirectional transforms

Apply both extensions on the same schema to transform data in both directions:

components: schemas: Service: type: object x-speakeasy-transform-to-api: jq: '{ displayName: .name, maxInstances: .max_instance_count }' x-speakeasy-transform-from-api: jq: '. + { name: .displayName, max_instance_count: .maxInstances }' properties: displayName: type: string maxInstances: type: integer

Convert an array to a map

Transform arrays from the API into a simpler map structure:

requestBody: content: application/json: schema: type: object x-speakeasy-transform-to-api: jq: | .tags |= ( if type == "object" then to_entries | map({key: .key, value: .value}) else . end ) properties: tags: type: array items: type: object properties: key: type: string value: type: string

Build composite identifiers

Create combined identifiers from separate API fields:

responses: "200": content: application/json: schema: type: object x-speakeasy-transform-from-api: jq: | .id = "projects/\(.project_id)/regions/\(.region)/databases/\(.name)" properties: project_id: type: string region: type: string name: type: string

Last updated on