Skip to main content

body-transformer

body-transformer

Rewrites the request and/or response body by rendering a template against the parsed JSON body and context variables. The template language is a deliberate subset — read the limitations below before writing templates.

Limitations (important)

featherbit implements:

  • input_format: json only. xml, encoded, args, plain, and multipart are rejected at config load. Omitting input_format defaults to json (there is no content-type sniffing).
  • Placeholder templates only. Templates are plain strings with two placeholder forms — no expressions, loops, or escaping helpers:
    • {{body.x.y}} — dotted path into the parsed JSON body (numeric segments index arrays, e.g. {{body.items.0.id}}; {{body}} is the whole document).
    • {{$var}} — a context variable ($uri, $http_x_request_id, $arg_page, $status, ... — the same names the vars system resolves). Text outside {{ }} also gets plain $var interpolation.
  • template_is_base64 is not supported and is rejected at config load — store templates literally in YAML.

Configuration

At least one of request / response is required.

KeyTypeDefaultDescription
requestobjectTransform applied to context.request.body.
request.templatestringrequiredOutput body template.
request.input_formatstringjsonOnly json is accepted.
responseobjectSame shape, applied to context.response.body.
type: body-transformer
config:
request:
input_format: json
template: '{"name":"{{body.user.name}}","trace":"{{$http_x_request_id}}"}'

Rejected at config load: missing/empty templates, an unclosed {{ placeholder, non-json input_format, and template_is_base64: true.

Behavior

Placement: a node with a request transform must sit before upstream; a node with a response transform must sit after it (the response body is empty until the upstream runs). Use two nodes when transforming both sides.

Placeholder resolution: strings from the body are inserted raw (unquoted — add quotes in the template when building JSON), numbers and booleans verbatim, null and missing paths as the empty string, and objects/arrays as compact JSON. Unknown placeholders render empty. Values substituted from the body are never re-interpolated.

  • Request transform — an empty body renders the template with all {{body...}} placeholders empty; a non-empty body that is not valid JSON fails with a 400. After rendering, the body is replaced and the stale content-length / content-encoding headers are removed.
  • Response transform — a content-encodingd upstream body (gzip, deflate, br) is decoded first; unsupported encodings, undecodable data, or a non-JSON body fail with a 502. After rendering, the body is replaced decoded and content-length / content-encoding are removed.

Failures exit through the error port with error code BODY_DECODE_FAILED and a JSON response body {"error": "body_decode_failed", "message": ...}. The plugin does not write to context.message.

Errors

The node returns the Context with an error, so the graph engine routes through the error port and appends the error to context.errors. The status below is the one prepared on context.response; wire error to client (or an error-handler) for the caller to see it.

CodeStatusWhen
BODY_DECODE_FAILED400 / 502The body could not be decoded — invalid JSON, or an unsupported/corrupt content encoding. 400 in the request phase (the client sent it), 502 in the response phase (the upstream did).