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.
Language support
Data transforms currently generate transform code for the Go target only. Other SDK targets (TypeScript, Python, Java, C#) will silently ignore these extensions.
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_nameandlast_nameintodisplay_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'Note
The only supported transform type is jq. Invalid syntax produces a build error with the expression and the parse error.
Examples
Copy a field
components:
schemas:
Resource:
type: object
x-speakeasy-transform-from-api:
jq: '. + { backup_id: .id }'
properties:
id:
type: stringRename a field
components:
schemas:
Resource:
type: object
x-speakeasy-transform-to-api:
jq: '{ displayName: .name }'
properties:
name:
type: stringAdd 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: stringFlatten 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: stringConditional 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: stringRemove 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: stringBidirectional 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: integerConvert 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: stringBuild 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: stringLast updated on