# map_fields

> Source: https://docs.clonepartner.com/reference/steps/map-fields/

<!-- sources: src/steps/map-fields-step.ts, src/steps/types.ts, references/envoy/step-types.md -->

# map_fields

Maps each source record onto a destination schema from a declarative mapping definition — built for migrations.

## When to use it

Use `map_fields` when many fields need renaming, per-field transforms, or conditional skips, and you want the mapping to live in reviewable YAML rather than one large expression. For a handful of computed fields, a plain [transform](/reference/steps/transform/) is simpler.

## Configuration

| Key | Type | Required | Default | Description |
|---|---|---|---|---|
| `mapping_file` | string | One of `mapping_file` or `mapping` | — | Path to a mapping YAML file. The file may also define `default_attributes` and `skip_record`. |
| `mapping` | object | One of `mapping_file` or `mapping` | — | Inline mapping definition, keyed by source field. |
| `lookups` | object | No | — | Destination field → JSONata expression, either a string or `{ from, required }`. Evaluated per record; a `required` lookup that resolves to null drops the record. |
| `skip_if_missing` | string[] | No | — | Source fields that must be present; a record where any of them is null, undefined, or an empty string is dropped. |

When neither `mapping_file` nor `mapping` is set, the step fails with `map_fields step requires mapping_file or mapping config`.

### Mapping entries

Each key in `mapping` is a source field name. Its value takes one of three shapes:

- **String** — the destination field name; the source value is copied as-is.
- **Object** — a mapping entry with these keys:

| Key | Type | Required | Default | Description |
|---|---|---|---|---|
| `field` | string | Yes | — | Destination field name. A value starting with `$` is evaluated as a JSONata expression to compute the destination field name per record. |
| `transform` | string | No | — | JSONata expression producing the value; the source record is available as `record`. |
| `skip` | string | No | — | JSONata condition; when truthy, this field is omitted for the record. |
| `path` | string | No | — | OData navigation path. The value is resolved to a destination ID through the task's `lookup_connector`, and the output field becomes `<field>@odata.bind` with the value `/<path>(<id>)`. |
| `type` | string | No | — | `number`, `date`, or `string`. Generates a typed transform: `number` wraps the value in `$number(...)`; `date` parses with `$dtFromFormat(...)` and emits an ISO string. Also generates a skip for null (and empty-string for `number`/`date`) values. |
| `format` | string | No | `yyyy-MM-dd` | Date parse format used with `type: date`. |

- **Array** — a list of strings or entry objects, mapping one source field to multiple destination fields.

### Mapping file extras

A `mapping_file` YAML may also define:

- `default_attributes` — an object of destination fields added to every output record unless the mapping already set them.
- `skip_record` — a JSONata string evaluated before mapping, or an object with `preprocess` (before mapping) and `postprocess` (after mapping, with the built record available as `processed_record`). A truthy result drops the record.

These two keys are read from the mapping file only, not from an inline `mapping` config.

## Example

```yaml
name: map-contacts
connectors:
  source:
    type: csv
steps:
  - name: read_contacts
    type: call_method
    config:
      connector: source
      method: read
      args:
        resource: contacts

  - name: map_contact
    type: map_fields
    config:
      mapping:
        firstname: firstname
        lastname: lastname
        companyname:
          field: company
          transform: "$trim(record.company)"
          skip: "record.company = null"
        birthdate:
          field: birthdate
          type: date
          format: yyyy-MM-dd
```

## Behavior notes

- **The output is a new record containing only mapped fields**, lookup results, and `default_attributes` — unmapped source fields do not carry over.
- **Null and undefined values are omitted** from the output rather than written as nulls.
- Expressions in `transform`, `skip`, `lookups`, and `skip_record` are evaluated with the source record's fields at the top level, plus `record`, `input`, `arguments`, `variables`, `steps`, and `job_config`.
- Record-level drops (`skip_if_missing`, a `required` lookup miss, or a truthy `skip_record`) emit nothing downstream for that record.
- `map_fields` is exempt from the static-config JSONata lint: its object config intentionally embeds expression strings.

## Related

- [transform](/reference/steps/transform/) — a single expression instead of a mapping table
- [sync_record](/reference/steps/sync-record/) — write the mapped record idempotently
- [Idempotent sync and the mapping DB](/building/idempotent-sync/) — where mapping fits in a migration pipeline
