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: jsononly.xml,encoded,args,plain, andmultipartare rejected at config load. Omittinginput_formatdefaults tojson(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 thevarssystem resolves). Text outside{{ }}also gets plain$varinterpolation.
template_is_base64is not supported and is rejected at config load — store templates literally in YAML.
Configuration
At least one of request / response is required.
| Key | Type | Default | Description |
|---|---|---|---|
request | object | — | Transform applied to context.request.body. |
request.template | string | required | Output body template. |
request.input_format | string | json | Only json is accepted. |
response | object | — | Same 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 stalecontent-length/content-encodingheaders 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 andcontent-length/content-encodingare 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.
| Code | Status | When |
|---|---|---|
BODY_DECODE_FAILED | 400 / 502 | The 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). |