Skip to main content

Overview

Sequential proxy allows you to chain backend calls where the response from the first call is used in the request to subsequent calls. This is useful when you need to fetch related data that requires IDs or values from a previous response.

Example: Hotel and Destination

The /sequential endpoint demonstrates a two-step process:
  1. Fetch hotel data to get the destination_id
  2. Use that destination_id to fetch destination details

Try It

curl http://localhost:8080/sequential
This returns combined hotel and destination data in a single response.

Configuration

From config/krakend/krakend.json:
{
  "endpoint": "/sequential",
  "backend": [
    {
      "url_pattern": "/hotels/1.json",
      "allow": ["destination_id"]
    },
    {
      "url_pattern": "/destinations/{resp0_destination_id}.json"
    }
  ],
  "extra_config": {
    "proxy": {
      "sequential": true
    }
  }
}

How It Works

Step 1: Enable Sequential Mode

"extra_config": {
  "proxy": {
    "sequential": true
  }
}
This tells KrakenD to execute backends in order, not in parallel.

Step 2: First Backend Call

{
  "url_pattern": "/hotels/1.json",
  "allow": ["destination_id"]
}
This calls the hotels API and extracts only the destination_id field. Example Response:
{
  "destination_id": 42
}

Step 3: Use Response in Next Call

{
  "url_pattern": "/destinations/{resp0_destination_id}.json"
}
The placeholder {resp0_destination_id} is replaced with the value from the first response. Actual Request:
GET /destinations/42.json

Response Variable Pattern

{resp<N>_<field_name>}
  • N = Backend index (0-based)
  • field_name = Field from that backend’s response
Examples:
"{resp0_id}"          // First backend, "id" field
"{resp0_user.email}"  // First backend, nested "user.email" field
"{resp1_token}"       // Second backend, "token" field

Execution Timeline

Sequential (enabled):
Request → Backend 1 (200ms) → Backend 2 (150ms) → Response
Total: 350ms
Parallel (default):
Request → Backend 1 (200ms) → Response
       → Backend 2 (150ms) → Response (can't access Backend 1's response)
Total: 200ms
Sequential is slower but enables data dependencies.

Use Cases

{
  "endpoint": "/user/{id}/posts-with-comments",
  "backend": [
    {
      "url_pattern": "/users/{id}/posts",
      "allow": ["latest_post_id"]
    },
    {
      "url_pattern": "/posts/{resp0_latest_post_id}/comments"
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Fetches a user’s latest post ID, then gets comments for that post.

2. Token-Based Authentication

{
  "endpoint": "/secure-data",
  "backend": [
    {
      "url_pattern": "/auth/token",
      "allow": ["access_token"]
    },
    {
      "url_pattern": "/api/data",
      "extra_config": {
        "modifier/martian": {
          "header.Modifier": {
            "scope": ["request"],
            "name": "Authorization",
            "value": "Bearer {resp0_access_token}"
          }
        }
      }
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Gets an auth token, then uses it to call a protected API.

3. Multi-Step Workflows

{
  "endpoint": "/checkout",
  "backend": [
    {
      "url_pattern": "/cart/validate",
      "allow": ["cart_id"]
    },
    {
      "url_pattern": "/inventory/reserve/{resp0_cart_id}",
      "allow": ["reservation_id"]
    },
    {
      "url_pattern": "/payment/process/{resp1_reservation_id}"
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Validate cart → Reserve inventory → Process payment.

4. Dynamic Routing

{
  "endpoint": "/article/{id}/recommended",
  "backend": [
    {
      "url_pattern": "/articles/{id}",
      "allow": ["category"]
    },
    {
      "url_pattern": "/articles/by-category/{resp0_category}"
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Get article category, then fetch related articles in that category.

Advanced Patterns

Multiple Fields from Response

{
  "backend": [
    {
      "url_pattern": "/users/{id}",
      "allow": ["email", "role", "department_id"]
    },
    {
      "url_pattern": "/permissions?email={resp0_email}&role={resp0_role}"
    },
    {
      "url_pattern": "/departments/{resp0_department_id}"
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Use multiple fields from the first response in subsequent calls.

Nested Field Access

For nested objects:
// Response from Backend 0:
{
  "user": {
    "profile": {
      "company_id": 123
    }
  }
}

// Use in Backend 1:
{
  "url_pattern": "/companies/{resp0_user.profile.company_id}"
}
Access nested fields using dot notation.

Conditional Logic

Combine with CEL for conditional execution:
{
  "backend": [
    {
      "url_pattern": "/user/{id}",
      "allow": ["is_premium"]
    },
    {
      "url_pattern": "/premium-content",
      "extra_config": {
        "validation/cel": [
          {
            "check_expr": "has(req_params.resp0_is_premium) && req_params.resp0_is_premium == true"
          }
        ]
      }
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Only call the second backend if the user is premium.

Response Structure

By default, all backend responses are merged: Backend 0 Response:
{
  "destination_id": 42,
  "hotel_name": "Grand Hotel"
}
Backend 1 Response:
{
  "destination_name": "Paris",
  "country": "France"
}
Final Response:
{
  "destination_id": 42,
  "hotel_name": "Grand Hotel",
  "destination_name": "Paris",
  "country": "France"
}

Grouping Responses

Use group to organize responses:
{
  "backend": [
    {
      "url_pattern": "/hotels/1.json",
      "allow": ["destination_id"],
      "group": "hotel"
    },
    {
      "url_pattern": "/destinations/{resp0_destination_id}.json",
      "group": "destination"
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}
Final Response:
{
  "hotel": {
    "destination_id": 42,
    "hotel_name": "Grand Hotel"
  },
  "destination": {
    "destination_name": "Paris",
    "country": "France"
  }
}

Performance Considerations

Latency

Sequential calls have cumulative latency:
3 backends × 200ms each = 600ms total
Vs. parallel aggregation:
3 backends × 200ms each = 200ms total (parallel)
Use sequential only when necessary!

Error Handling

If any backend in the chain fails, the entire request fails:
Backend 1 (success) → Backend 2 (failed) → Request fails
No response is returned even though Backend 1 succeeded.

Optimization Tips

  1. Minimize Chain Length - Fewer steps = lower latency
  2. Cache Intermediate Results - Use caching on each backend
  3. Selective Field Extraction - Use allow to minimize response size
  4. Timeout Management - Set appropriate timeouts for the entire chain

Comparison: Sequential vs Parallel

AspectSequentialParallel (Aggregation)
ExecutionOne after anotherAll at once
LatencySum of all callsMax of all calls
DependenciesCan use previous responsesIndependent calls only
Use CaseRelated data, workflowsMerge different data sources
Config"sequential": trueDefault behavior

Error Handling

{
  "endpoint": "/sequential-with-fallback",
  "backend": [
    {
      "url_pattern": "/primary/data",
      "allow": ["id"]
    },
    {
      "url_pattern": "/secondary/{resp0_id}",
      "extra_config": {
        "plugin/req-resp-modifier": {
          "name": ["handle-errors"]
        }
      }
    }
  ],
  "extra_config": {
    "proxy": {
      "sequential": true
    }
  }
}
Implement custom error handling to gracefully handle failures in the chain.

Best Practices

1. Use allow to Extract Only Needed Fields

// ✓ Good - Only extract what you need
{
  "url_pattern": "/users/{id}",
  "allow": ["company_id"]
}

// ✗ Bad - Passes entire response
{
  "url_pattern": "/users/{id}"
}

2. Set Timeouts for the Entire Chain

{
  "endpoint": "/sequential",
  "timeout": "5s",  // Total time for all backends
  "backend": [...],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}

3. Avoid Deep Chains

✓ Good: 2-3 sequential calls
⚠ Careful: 4-5 sequential calls
✗ Bad: 6+ sequential calls (use different architecture)

4. Combine with Caching

Cache intermediate results when possible:
{
  "backend": [
    {
      "url_pattern": "/auth/token",
      "allow": ["token"],
      "cache_ttl": "1h"  // Cache token
    },
    {
      "url_pattern": "/api/data"
    }
  ],
  "extra_config": {
    "proxy": { "sequential": true }
  }
}

Learn More

Build docs developers (and LLMs) love