> Raw Markdown twin (generated at build time from the source Markdown). Rendered page: https://docs.gatellm.io/en/practices/routing-and-transform · Doc index: https://docs.gatellm.io/en/llms.txt


# Request rewriting and routing: how to choose

The gateway has three ways to rewrite/route requests: `request_payload` declarative rules, `switch-route` structural routing, and JavaScript scripts. Performance goes from high to low, expressiveness from low to high. This page is the single authority for choosing among the three.

## Boundaries of the three approaches

| Approach | Modifies body? | Performance | Expressiveness | Memory |
|------|---------|------|--------|------|
| `request_payload` rules | Yes (after translation) | Best (declarative, no script-engine serialization) | Field-level add/delete/modify, inject | Not read into memory |
| `switch-route` rules | No (only decide + redirect) | Good (only structural/small-value checks) | Field existence, small-value equality | Not read into memory |
| Scripts (before/after slots) | Yes | Average (serialization overhead) | Arbitrary logic, cross-field computation | after slot reads into memory |

## Decision table

| Scenario | Recommended approach |
|------|---------|
| Simple field add/delete/modify (`temperature`, `max_tokens`, `response_format`) | `request_payload` rules |
| Switch model by structural check (has image → stronger model; has web access → web model) | `switch-route` rules |
| Inject system prompt / append messages (only when absent) | `request_payload` `inject-system-prompt` / `append-if-missing` |
| Cross-field computation / conditional branching / complex checks | Scripts (before slot reads the client's original format) |
| Response content redaction / inject metadata | Response scripts (`response_transform`) |

In one sentence: `request_payload` covers about 90% of simple scenarios, `switch-route` handles pure routing decisions, and scripts handle the remaining complex logic.

## Prefer rules for same-protocol routing decisions

As long as the target uses the **same protocol** as the current route, pure routing decisions ("detect image/web access → switch model") should **prefer `switch-route` rules**, not scripts. See the anti-pattern section in [Script performance and memory](/en/practices/script-performance.md) — after-slot scripts read the entire body (including inline base64) into script-engine memory, and large multimodal conversations can easily OOM; `switch-route` predicates only do structural/small-value checks and never read binary content bytes, so disk spooling stays enabled for large request bodies.

## When scripts are required

- The switch is **cross-protocol** and the check exceeds what structural checks can express (`switch-route` supports cross-protocol, but predicates only do existence/small-value equality).
- You need to **read/modify body content** (not just routing decisions), e.g. conditional rewriting by message content, or injecting different parameters grouped by access key.
- You need **response rewriting** (`switch-route` / `request_payload` only act on requests).

For cross-protocol scenarios, put the script in the **before slot** to read the client's original format; see [Script-based protocol translation](/en/howto/script-protocol-translate.md).

## Attach to upstream or model

- Upstream-level rules/scripts: shared by all models going through this upstream. Good for protocol adaptation and common field injection.
- Model-level rules/scripts: this model only. Model-level overrides upstream-level.

See [Upstream and model fields](/en/reference/upstreams-models-fields.md).

## FAQ

**Q: Is switch-route the same as request_payload's switch-route?**
Yes. `switch-route` is a `mode` of the `request_payload` rule, but it does not modify the body and evaluates by the client shape before protocol translation. See the mode overview in [Upstream and model fields](/en/reference/upstreams-models-fields.md).

**Q: After the before slot triggers useModel switching, does the original route's after slot still run?**
No. Once the before slot triggers an immediate `useModel` route switch, the entire pipeline is replaced with the target route's, and the original route's after slot does not execute. So the `request_payload` rules on both the original and target routes must each be configured.

**Next**: [Script performance and memory](/en/practices/script-performance.md) for performance and anti-patterns; [Write your first script transform](/en/howto/write-script-transform.md) for a script primer; [Upstream and model fields](/en/reference/upstreams-models-fields.md) for fields.
