Summary: Using KrakenD chaining and a Lua formatter script, combine separate Product and Price API responses into a unified endpoint that maintains nested objects and respects client-side expectations.
- We have separate Product API and Price API.
- The client wants to receive both product information and pricing in a single response.
Goal: Update the endpoint without requiring any changes on the client-side logic.
- Update an endpoint that merges responses from the Product API and the Price API.
- Keep the data structure using nested objects product and price.
- Ensure the response envelope (code, message, error) remains consistent.

{
"code": "KND-200",
"data": {
"product": {
"category": "Electronics",
"id": 1,
"name": "Wireless Earbuds",
"stock": 150
}
},
"error": "null",
"message": "Success"
}
{
"code": "KND-200",
"data": {
"price": {
"id": 1,
"price": 199.99
}
},
"error": "null",
"message": "Success"
}

{
"code": "PLT-KND-200",
"data": {
"price": {
"id": 1,
"price": 199.99
},
"product": {
"category": "Electronics",
"id": 1,
"name": "Wireless Earbuds",
"stock": 150
}
},
"error": "null",
"message": "Success"
}

We implementation api chaining and response processing in KrakenD, the goals that get the both response then we proccess that response to be like our standard response.
- This is endpoint /product/{product_id} that client apps call
- This KrakenD request to to backend endpoint /product/{product_id} and /price/{product_id}
- After the both chaining request is done, KrakenD call lua script which has function response_formatter with three param one is the response (from both api call), then the object group from each response.
function response_formatter(resp, first_group_name, second_group_name)
local response_data = resp:data()
-- Extract data from nested response objects
local first_group_obj = response_data:get(first_group_name)
local second_group_obj = response_data:get(second_group_name)
local first_group_data = nil
local second_group_data = nil
-- Get the 'data' field from first group if it exists
if first_group_obj and first_group_obj.get then
first_group_data = first_group_obj:get('data')
end
-- Get the 'data' field from second group if it exists
if second_group_obj and second_group_obj.get then
second_group_data = second_group_obj:get('data')
end
-- Clean up: remove the original nested objects
response_data:del(first_group_name)
response_data:del(second_group_name)
-- Set standard response envelope
response_data:set('code', 'KND-200')
response_data:set('message', 'Success')
response_data:set('error', 'null')
-- Build the new data structure with dynamic keys
local restructured_data = {}
if first_group_data then
restructured_data[first_group_name] = first_group_data
end
if second_group_data then
restructured_data[second_group_name] = second_group_data
end
-- Set the restructured data
response_data:set('data', restructured_data)
end
- Current response from endpoint /product/{product_id} after implemting response proccessing
