# Reshaping a response

> Goal: serve only what the contract publishes, mask personal data, translate an upstream's non-standard statuses - without touching the upstream.

Guide - Since version 1.2.0 - Flexway v1.6.3 - https://flexway.io/en/docs/operations-response-body/

> Operations guide: configure, deploy and observe the gateway. What the capability does, product side: [product guide](https://flexway.io/en/docs/product-overview/); calling it: [developer guide](https://flexway.io/en/docs/developer-overview/).

**Goal**: serve only what the contract publishes, mask personal data, translate an upstream's
non-standard statuses - without touching the upstream.

**Command** - three policies in `flexway.yaml`, attached to the operation or to routes:

```yaml
policies:
  definitions:
    public-order:
      type: transform-body
      config: { project: [id, status, total, items, customer], on_missing: fail }   # a closed list; fail | omit
    no-pii:
      type: redact
      config: { fields: [body.customer.email, body.customer.phone], with: "***" }   # or remove: true
    legacy-errors:
      type: map-status
      config:
        - { from: 200, when: "has(body.error)", to: 422, code: legacy_rejected }    # a CEL condition on the body
        - { from: 520, to: 502 }
```

**Check**: `GET /orders/7` returns the listed fields only, `customer.email` is `"***"`; a `200`
carrying `error` arrives as a `422` `application/problem+json` (`code: legacy_rejected`), without the
upstream's words; `flexway_policy_executions_total{phase="response"}` counts each policy applied, by operation, and `flexway_policy_skipped_total` the ones their condition skipped (`1.2.0`).

| Rule | What it means |
| --- | --- |
| Order | `map-status` decides the status, then `transform-body`, then `redact` - whatever the chain's order |
| Cost | The body of these operations is **read whole** (JSON only); every other keeps the direct relay |
| Successes projected | `transform-body` touches `2xx` only; `redact` masks every response, errors included |
| Error produced | `map-status` to a `4xx`/`5xx`: an RFC 9457 problem, the upstream's body dropped; to a success: the operation must declare it |
| Never raw | Past `server.body.max_bytes` (1 MiB): `502` `response_too_large`; an unreadable or non-JSON body: `502` `upstream_error` - never served as it came |
| Circuit breaker | It counts the **upstream's** status: a `503` served as `422` is still a failure |
| Refused at load | A stream, a non-JSON response, gRPC, inference; a projection that drops a **required** field or keeps an undeclared one; a mask of the wrong type; a contract example that, transformed, no longer holds its schema - the message names the operation |
| Conformance | `server.body.validate_sample` (`0.01`) of the served bodies validated against the contract: a departure counts `flexway_policy_nonconformant_total`, never a refusal; reloadable |
| Cache | The cache keeps the upstream's answer; a hit is transformed as a miss is |

Full example: [Response body and status](https://flexway.io/en/docs/operations-examples-gateway/#response-body-and-status).
