> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Input field selection

> Project the function input down to the REST fields you actually read

# Input field selection

Functions can declare **exactly which REST input fields they need** on
every invocation. The dispatcher trims the assembled input to that
subtree before invoking your WASM module — your function sees a smaller,
faster, exactly-shaped object.

This is purely REST: you author a plain JSON tree of booleans matching
the input contract for your function type. There is no GraphQL involved.

## Why opt in

* **Faster dispatch.** Smaller objects serialize to JSON faster; smaller
  payloads cross the WASM ABI faster. A discount function that reads
  `cart.totalPrice` and ignores everything else processes a 12 KB cart
  in \~200 bytes of JSON.
* **Smaller WASM payloads.** Functions doing simple work don't have to
  scan past 80% of the input. Each unneeded field costs JSON parse time
  in the QuickJS runtime that backs Javy dynamic-mode WASM.
* **Explicit dependencies.** Reading the manifest makes it obvious which
  fields the function actually reads. Future-you will thank present-you.
* **Tighter security.** Even though your function runs in a sandboxed
  WASM module, projecting away fields you don't need limits the blast
  radius of accidental logging or output payloads.

## Manifest

`inputFields` is a sibling field on each entry in `extensions.functions`:

```json theme={null}
{
  "handle": "smart-discounts",
  "name": "Smart Discounts",
  "version": "1.0.0",
  "extensions": {
    "functions": [
      {
        "type": "discount",
        "handle": "vip-discount",
        "entrypoint": "dist/vip.wasm",
        "inputFields": {
          "cart": {
            "lines": {
              "id": true,
              "quantity": true,
              "merchandise": {
                "id": true,
                "sku": true,
                "price": true
              }
            },
            "totalPrice": true,
            "discountCodes": true
          },
          "customer": {
            "id": true,
            "tags": true
          }
        }
      }
    ]
  }
}
```

## Semantics

The tree mirrors the runtime input shape for your function type (see
each function's reference page). Leaves describe whether to keep a key;
nested objects recurse.

| Leaf value                    | Meaning                                                                                      |
| ----------------------------- | -------------------------------------------------------------------------------------------- |
| `true`                        | Include the field at this position. If it's an array or object, copy the entire subtree.     |
| `{ ... }` (plain object)      | Recurse — include only the listed sub-fields of this value.                                  |
| `false`                       | Silently drop the key (same as omitting). Useful when generating manifests programmatically. |
| *omitted key*                 | Drop the field entirely.                                                                     |
| `inputFields` missing or `{}` | Full input is passed (backward compatible).                                                  |
| `null` / `undefined`          | Same as missing — no projection requested.                                                   |

### Arrays project per-item

```json theme={null}
"lines": {
  "id": true,
  "quantity": true,
  "merchandise": { "sku": true }
}
```

Each element of `cart.lines` is projected to
`{ id, quantity, merchandise: { sku } }`. Nested objects inside array
items recurse the same way. The projector walks every element, so:

* An empty array stays an empty array.
* A 200-item array projects 200 times — projection itself is O(n) over
  the array length.

### Scalars at a sub-projection position

If your map declares `cart: { totalPrice: { ... } }` but `totalPrice` is
actually a `number`, the projector passes it through unchanged (it
can't recurse into a scalar). This is forgiving by design: apps that
probe an optional object that some shops have flattened to a scalar
won't break.

### Missing keys

A key that's in your `inputFields` but missing on the runtime input is
**omitted** from the output (no `undefined` written). Reading
`input.customer?.tags` in your function code stays the right pattern.

## Examples per function type

Each function type has its own input contract — these examples show
typical projections.

### Discount (reads totals + codes, optionally the destination address)

```json theme={null}
{
  "type": "discount",
  "handle": "vip",
  "inputFields": {
    "cart": {
      "totalPrice": true,
      "currency": true,
      "discountCodes": true,
      "lines": { "id": true, "quantity": true, "price": true }
    },
    "customer": { "id": true, "tags": true },
    "shippingAddress": { "country": true, "province": true, "zip": true }
  }
}
```

`shippingAddress` (address1, address2, city, province, country, zip) is
available to discount functions for location-aware rules — e.g. ZIP-keyed
US sales-tax adjustments. Fields are empty strings until the customer
fills the address step at checkout.

### Shipping rate (reads cart, destination)

```json theme={null}
{
  "type": "shipping_rate",
  "handle": "zone-rates",
  "inputFields": {
    "cart": {
      "totalPrice": true,
      "itemCount": true,
      "currency": true,
      "lines": { "quantity": true, "merchandise": { "id": true } }
    },
    "destination": {
      "country": true,
      "province": true,
      "zip": true
    },
    "currency": true
  }
}
```

### Payment customization (reads payment methods, cart total)

```json theme={null}
{
  "type": "payment_customization",
  "handle": "hide-cod-over-1000",
  "inputFields": {
    "cart": { "totalPrice": true, "currency": true },
    "paymentMethods": true
  }
}
```

### Cart transform (reads lines + attributes)

```json theme={null}
{
  "type": "cart_transform",
  "handle": "bundle-pricing",
  "inputFields": {
    "cart": {
      "lines": {
        "id": true,
        "quantity": true,
        "price": true,
        "originalPrice": true,
        "properties": true,
        "merchandise": {
          "id": true,
          "sku": true,
          "metafields": true
        }
      }
    }
  }
}
```

### Delivery customization (reads cart, options)

```json theme={null}
{
  "type": "delivery_customization",
  "handle": "rename-zones",
  "inputFields": {
    "cart": { "totalPrice": true, "currency": true },
    "deliveryOptions": true
  }
}
```

### Order validation (reads cart, customer, address)

```json theme={null}
{
  "type": "order_validation",
  "handle": "max-qty-guard",
  "inputFields": {
    "cart": {
      "lines": { "id": true, "quantity": true, "merchandise": { "sku": true } },
      "totalPrice": true
    },
    "customer": { "id": true, "email": true },
    "shippingAddress": { "country": true, "zip": true },
    "step": true
  }
}
```

### Fulfillment constraints (reads lines + attributes + location catalogue)

```json theme={null}
{
  "type": "fulfillment_constraints",
  "handle": "hazmat-routing",
  "inputFields": {
    "cart": {
      "lines": {
        "id": true,
        "quantity": true,
        "merchandise": {
          "sku": true,
          "attributes": true,
          "metafields": true
        }
      }
    },
    "fulfillmentLocations": true,
    "shippingAddress": { "country": true, "province": true }
  }
}
```

### Local / pickup point options (reads cart + address)

```json theme={null}
{
  "type": "local_pickup_options",
  "handle": "store-pickups",
  "inputFields": {
    "cart": {
      "totalPrice": true,
      "lines": { "quantity": true, "merchandise": { "id": true } }
    },
    "shippingAddress": { "country": true, "province": true, "city": true, "zip": true },
    "currency": true
  }
}
```

## Validation rules

The install endpoint validates the `inputFields` tree before persisting
the function. Errors are returned as HTTP 400 with a path-qualified
message so you know which key is wrong:

```
inputFields.cart.lines must be true/false or a nested object (got string)
```

Validation walks the tree recursively and applies these rules:

| Rule                                                          | Failure example        | Error message                                                                  |
| ------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------ |
| The root must be a plain object (or omitted).                 | `"inputFields": "all"` | `inputFields must be a plain object, got string`                               |
| Every leaf must be `true`, `false`, or a nested plain object. | `"id": 1`              | `inputFields.cart.lines.id must be true/false or a nested object (got number)` |
| Arrays are not allowed as values.                             | `"lines": ["id"]`      | `inputFields.cart.lines must be a plain object, got array`                     |
| `null` is not allowed as a value.                             | `"customer": null`     | `inputFields.customer must be true/false or a nested object (got null)`        |

Validation is path-tracking, so deeply-nested errors report their full
dotted path. Update the manifest and reinstall.

## Backward compatibility

Manifests without `inputFields` (or with an empty `{}`) keep working
unchanged — they receive the full input. There is no version flag to
set. You can add `inputFields` to an existing manifest without changing
your function's behaviour, as long as the projection includes every
field your function reads.

If you ship a new function version that reads a new field but forget to
add it to `inputFields`, the field arrives as `undefined` to your
function. Bump the projection at the same time as the field-read.

## How the platform consults the projection at dispatch time

When the platform dispatches a function, it:

1. Looks up the active functions of that type for the store.
2. For each function, if it declared `inputFields`, projects the
   assembled input down to that subtree. Functions without `inputFields`
   receive the full input unchanged.
3. Serializes the projected input, hands it to the sandboxed WASM
   worker, and parses the result.

Two important properties:

1. **Per-function projection.** Each function gets its own projected
   input — different apps with different `inputFields` see different
   trees of the same dispatch.
2. **Empty / missing projection is a no-op.** The input is passed
   through unchanged. Manifests without `inputFields` cost zero.

Install-time validation and dispatch-time projection apply the same
rules, so a manifest that passes validation at install behaves
identically at dispatch.

## Performance impact

Concrete numbers from typical carts:

| Cart size | No projection  | With minimal projection |
| --------- | -------------- | ----------------------- |
| 1 line    | \~3 KB input   | \~200 B                 |
| 10 lines  | \~15 KB input  | \~600 B                 |
| 100 lines | \~120 KB input | \~5 KB                  |

Time saved per dispatch is dominated by JSON serialization on the host
side and JSON parsing on the worker side. On a 100-line cart, a minimal
projection typically shaves **5–15 ms** off the dispatch round trip —
worth it on every checkout interaction.

## Best practices

* **Project to exactly what you read.** Don't over-project "just in
  case" — every extra field is a future maintenance question.
* **Pin the version.** Every time you change which fields your function
  reads, bump the function version and update `inputFields` in the same
  commit.
* **Use `true` for opaque subtrees.** If you don't know the inner shape
  of `merchandise.metafields` and want everything under it, just write
  `"metafields": true` — the entire subtree is copied as-is.
* **Don't project `cart.lines` to a scalar set if you also read
  per-line metafields.** Metafields live under `merchandise.metafields`
  per line, so recurse into `merchandise` even if you only read one
  metafield path.

## See also

* [Network access](/functions/network-access) — the other manifest-level
  field that controls function runtime behaviour.
* Function input contracts: [Discount](/functions/discount),
  [Shipping rate](/functions/shipping-rate),
  [Payment customization](/functions/payment-customization),
  [Cart transform](/functions/cart-transform),
  [Delivery customization](/functions/delivery-customization),
  [Order validation](/functions/order-validation),
  [Fulfillment constraints](/functions/fulfillment-constraints).
