Cratopus icon
FAST!

Solving 'Migration Hell': Mapping Legacy APIs with a JSONata Engine

Written by Derrick Antaya

Every scaling team eventually hits the “Migration Wall.”

The “Migration Hell” Scenario

Most legacy enterprise APIs suffer from “Payload Bloat.” A simple request for a customer profile might return 400 lines of XML-converted-to-JSON, featuring nested arrays, PascalCase keys, and null fields.

A Real-World Example:

Imagine a legacy SOAP service returning this “Customer” object:

{
  "GetCustomerResponse": {
    "Status": "OK",
    "Data": {
      "CUST_ID": "1002",
      "PRFL_LVL": "Gold",
      "SYS_MTDT": { "SRC": "DB2", "VRSN": "1.2" },
      "ADDR_LIST": [
        { "TYPE": "HOME", "STR": "123 Main St", "ZIP": "90210" }
      ]
    }
  }
}

In a standard gateway, you’d write a Java or Lua plugin to parse this. In Crate, you use JSONata:

{
  "id": GetCustomerResponse.Data.CUST_ID,
  "membership": GetCustomerResponse.Data.PRFL_LVL,
  "primaryAddress": GetCustomerResponse.Data.ADDR_LIST[TYPE='HOME'].STR
}

The result is a clean, modern JSON object delivered to your frontend in sub-millisecond time. This logic lives entirely in your gateway configuration, not your application code.

Usually, you have two bad options:

  1. The Backend Rewrite: Spend 6 months refactoring legacy code you didn’t write.
  2. Frontend Bloat: Force your client-side code to handle complex data reshaping, killing your performance scores.

At Crate, we built a third option: The In-Flight Transformation Layer.

Why JSONata?

When we planned the plugin system for Crate, we looked at running arbitrary JavaScript (V8/Isolate). While powerful, it introduced too much non-deterministic risk for a high-throughput gateway.

We chose JSONata—a lightweight, declarative query and transformation language. It allows us to:

  • Reshape Inbound/Outbound Bodies: Perform aggregations and conditional logic on the fly.
  • Maintain Safety: JSONata is inherently sandboxed, preventing infinite loops that could take down a gateway node.

The Engineering Challenge: The “Safety Sandbox” in Go

Since Crate is built in Go, we had to ensure our JSONata engine didn’t become a bottleneck. We implemented a multi-stage safety check:

  1. Context-Aware Timeouts: Every transformation runs with a strict Go context timeout. If a mapping takes longer than 5ms, we fail-fast to protect the node’s throughput.
  2. Memory Limits: We track the allocation size of the transformed object. If a user tries to map a 50MB JSON object into a 500MB monstrosity, Crate intercepts and kills the process before an OOM (Out of Memory) event occurs.

A Real-World Use Case

Imagine a legacy endpoint that returns user data in this format: {"USR_01": {"f_name": "Derrick", "l_name": "Antaya", "meta": {"sys_id": 99}}}.

With a simple JSONata expression in Crate: {"id": USR_01.meta.sys_id, "fullName": USR_01.f_name & " " & USR_01.l_name}

Your modern frontend receives a clean, flat object: {"id": 99, "fullName": "Derrick Antaya"}

Performance Impact

By running this on our Dedicated Bare Metal nodes, we’ve managed to keep the overhead of these transformations under 2ms for average payloads. This allows teams to modernize their stack today without touching a single line of legacy backend code.


What’s Next? With routing and transformations stable, our next focus is tightening the loop on Traffic Anomaly Detection. We’re building out more granular alerting so that when your downstream services spike in error rates, you know about it before your users do.