# API uses "maxInstances" but provider uses "max_instance_count"
resource "platform_service" "api" {
  name                = "api-service"  # API: displayName
  max_instance_count  = 10             # API: maxInstances
}
```

These naming improvements create a better experience, but they're custom logic that needs to persist through migration.

## Data transformations with JQ

Data transformations use [JQ expressions](https://jqlang.org/manual/) to convert between your provider interface and your API structure. JQ is a widely-used JSON manipulation language—if you've worked with complex JSON transformations, you've likely encountered it.

The transformation layer sits between Terraform and your API, automatically converting data in both directions:

- **Requests**: Transform Terraform configuration values into the structure your API expects
- **Responses**: Transform API responses into the structure your Terraform provider exposes

### Converting tags to a map

Here's how to transform tag arrays into the simpler map structure:

```yaml
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
```

Users configure tags as a simple map in their Terraform configuration. The transformation converts it to the array structure before sending it to the API. Response transformations work in reverse, converting arrays back to maps.

### Building composite identifiers

Create combined identifiers from separate API fields:

```yaml
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
```

The API returns three separate fields. The transformation concatenates them into a single identifier that Terraform users work with.

### Renaming fields

Normalize field names to match Terraform conventions:

```yaml
schema:
  type: object
  x-speakeasy-transform-to-api:
    jq: |
      {
        displayName: .name,
        maxInstances: .max_instance_count
      }
  properties:
    displayName:
      type: string
    maxInstances:
      type: integer
```

The transformation maps between your provider's naming conventions and the API's field names.

## The JQ playground

Writing and testing JQ expressions requires iteration. We've built a playground specifically for Terraform provider transformations at [jq.speakeasy.com](https://jq.speakeasy.com).

The playground includes:

- Syntax highlighting and validation
- Real-time transformation preview
- Example transformations for common patterns
- Error messages that explain what went wrong

Use the playground to develop transformations before adding them to your OpenAPI specification. Test with sample data to verify the transformation produces the expected structure in both directions.

## Beyond migration: forward-looking transformations

While migration is the primary use case, data transformations also support forward-looking changes. If you know your API will change in the future, you can implement the new structure in your Terraform provider ahead of time.

For example, suppose your API currently uses a field name you plan to deprecate:

```yaml