Menu

Route Matching Overview

ApiCharge uses YARP's powerful route matching system to determine which incoming requests should be forwarded to which backend services. Routes can match based on multiple criteria including paths, hosts, HTTP methods, headers, and query parameters.

Note: ApiCharge extends YARP's routing with ApiCharge Sbscription capabilities. All YARP route matching features work seamlessly with ApiCharge's monetization layer.

Match Criteria

A route's Match section defines the criteria that incoming requests must satisfy to be handled by that route. You can combine multiple match criteria for precise routing control.

{
  "ApiChargeProxy": {
    "Routes": {
      "example-route": {
        "ClusterId": "backend-cluster",
        "Match": {
          "Path": "/api/users/{id}",
          "Hosts": ["api.example.com"],
          "Methods": ["GET", "POST"],
          "Headers": [
            {
              "Name": "Content-Type",
              "Values": ["application/json"],
              "Mode": "ExactHeader"
            }
          ],
          "QueryParameters": [
            {
              "Name": "version",
              "Values": ["v1", "v2"],
              "Mode": "Exact"
            }
          ]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      }
    }
  }
}

Path Matching

Path matching is the most common way to route requests. ApiCharge supports ASP.NET Core route templates with rich pattern matching capabilities.

Basic Path Patterns

Exact Match

"Match": {
  "Path": "/api/users"
}

Matches exactly /api/users with no additional path segments.

Single Parameter

"Match": {
  "Path": "/api/users/{id}"
}

Matches /api/users/123, /api/users/abc, etc. The {id} parameter captures the value.

Multiple Parameters

"Match": {
  "Path": "/api/users/{userId}/posts/{postId}"
}

Matches /api/users/123/posts/456 with both parameters captured.

Catch-All Pattern

"Match": {
  "Path": "/api/{**catch-all}"
}

Matches any path starting with /api/ and captures the remainder in the catch-all parameter.

Optional Parameters

"Match": {
  "Path": "/api/users/{id?}"
}

Matches both /api/users and /api/users/123.

Parameter Constraints

You can apply constraints to route parameters to ensure they match specific patterns:

"Match": {
  "Path": "/api/users/{id:int}/posts/{slug:alpha}"
}

Common constraints include:

Advanced Path Examples

API Versioning

{
  "ApiChargeProxy": {
    "Routes": {
      "v1-users": {
        "ClusterId": "users-v1-cluster",
        "Match": {
          "Path": "/api/v1/users/{**remainder}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 50000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      },
      "v2-users": {
        "ClusterId": "users-v2-cluster", 
        "Match": {
          "Path": "/api/v2/users/{**remainder}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 200
            }
          ]
        }
      }
    }
  }
}

Microservice Routing

{
  "ApiChargeProxy": {
    "Routes": {
      "users-service": {
        "ClusterId": "users-cluster",
        "Match": {
          "Path": "/services/users/{**catch-all}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 75000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 150
            }
          ]
        }
      },
      "orders-service": {
        "ClusterId": "orders-cluster",
        "Match": {
          "Path": "/services/orders/{**catch-all}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      }
    }
  }
}

HTTP Method Matching

Method matching allows you to route requests based on the HTTP verb (GET, POST, PUT, DELETE, etc.).

Single Method

"Match": {
  "Path": "/api/users/{id}",
  "Methods": ["GET"]
}

Multiple Methods

"Match": {
  "Path": "/api/users/{id}",
  "Methods": ["GET", "PUT", "DELETE"]
}

REST API Example

{
  "ApiChargeProxy": {
    "Routes": {
      "users-read": {
        "ClusterId": "users-read-cluster",
        "Match": {
          "Path": "/api/users/{**remainder}",
          "Methods": ["GET", "HEAD", "OPTIONS"]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 50000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 200
            }
          ]
        }
      },
      "users-write": {
        "ClusterId": "users-write-cluster",
        "Match": {
          "Path": "/api/users/{**remainder}",
          "Methods": ["POST", "PUT", "PATCH", "DELETE"]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      }
    }
  }
}

Header Matching

Header matching enables routing based on HTTP headers, useful for API versioning, content negotiation, or custom routing logic.

Header Match Modes

Mode Description Example
ExactHeader Header value must match exactly Content-Type: application/json
HeaderPrefix Header value must start with the specified prefix Accept: application/
Contains Header value must contain the specified text User-Agent contains mobile
Exists Header must be present (any value) Authorization header exists
NotContains Header value must not contain the specified text User-Agent doesn't contain bot
NotExists Header must not be present No X-Internal header

API Versioning via Headers

{
  "ApiChargeProxy": {
    "Routes": {
      "api-v1": {
        "ClusterId": "api-v1-cluster",
        "Match": {
          "Path": "/api/{**remainder}",
          "Headers": [
            {
              "Name": "API-Version",
              "Values": ["1.0", "1.1"],
              "Mode": "ExactHeader"
            }
          ]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 75000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 150
            }
          ]
        }
      },
      "api-v2": {
        "ClusterId": "api-v2-cluster",
        "Match": {
          "Path": "/api/{**remainder}",
          "Headers": [
            {
              "Name": "API-Version", 
              "Values": ["2.0"],
              "Mode": "ExactHeader"
            }
          ]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 200
            }
          ]
        }
      }
    }
  }
}

Query Parameter Matching

Query parameter matching allows routing based on URL query strings, useful for feature flags, A/B testing, or API versioning.

Query Parameter Match Modes

Mode Description Example
Exact Parameter value must match exactly ?version=1.0
Prefix Parameter value must start with prefix ?filter=user_
Contains Parameter value must contain text ?search=api
Exists Parameter must be present (any value) ?debug or ?debug=true

Feature Flag Routing

{
  "ApiChargeProxy": {
    "Routes": {
      "beta-features": {
        "ClusterId": "beta-cluster",
        "Match": {
          "Path": "/api/{**remainder}",
          "QueryParameters": [
            {
              "Name": "beta",
              "Mode": "Exists"
            }
          ]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 150000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 50
            }
          ]
        }
      },
      "stable-features": {
        "ClusterId": "stable-cluster",
        "Match": {
          "Path": "/api/{**remainder}"
        },
        "Order": 1,
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      }
    }
  }
}

Route Precedence and Ordering

When multiple routes could match the same request, ApiCharge uses a precedence system to determine which route is selected.

Default Precedence Order

Routes are evaluated in this order of precedence:

  1. Path - More specific paths have higher precedence
  2. HTTP Method - Routes with method constraints
  3. Host - Routes with host constraints
  4. Headers - Routes with header constraints
  5. Query Parameters - Routes with query parameter constraints

Explicit Route Ordering

You can override the default precedence by setting the Order property. Lower numbers have higher precedence.

{
  "ApiChargeProxy": {
    "Routes": {
      "high-priority-route": {
        "ClusterId": "priority-cluster",
        "Order": 0,
        "Match": {
          "Path": "/api/{**remainder}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 200000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 50
            }
          ]
        }
      },
      "specific-route": {
        "ClusterId": "specific-cluster", 
        "Order": 1,
        "Match": {
          "Path": "/api/users/{id}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 100000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      },
      "fallback-route": {
        "ClusterId": "fallback-cluster",
        "Order": 100,
        "Match": {
          "Path": "{**catch-all}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 50000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 25
            }
          ]
        }
      }
    }
  }
}

Complex Matching Examples

Multi-Criteria Matching

Combine multiple match criteria for sophisticated routing:

{
  "ApiChargeProxy": {
    "Routes": {
      "admin-api": {
        "ClusterId": "admin-cluster",
        "Match": {
          "Path": "/api/admin/{**remainder}",
          "Methods": ["GET", "POST", "PUT", "DELETE"],
          "Headers": [
            {
              "Name": "Authorization",
              "Mode": "Exists"
            },
            {
              "Name": "X-Admin-Token",
              "Mode": "Exists"
            }
          ]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 500000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 1000
            }
          ]
        }
      },
      "public-api": {
        "ClusterId": "public-cluster",
        "Match": {
          "Path": "/api/public/{**remainder}",
          "Headers": [
            {
              "Name": "Authorization",
              "Mode": "NotExists"
            }
          ]
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 25000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 50
            }
          ]
        }
      }
    }
  }
}

ApiCharge-Specific Considerations

Monetized Route Patterns

When designing routes for ApiCharge, consider how the routing patterns align with your monetization strategy:

{
  "ApiChargeProxy": {
    "Routes": {
      "basic-tier": {
        "ClusterId": "basic-cluster",
        "Match": {
          "Path": "/api/basic/{**remainder}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 50000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 100
            }
          ]
        }
      },
      "premium-tier": {
        "ClusterId": "premium-cluster", 
        "Match": {
          "Path": "/api/premium/{**remainder}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 150000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 500
            },
            {
              "$type": "DataLimitDownload",
              "Bytes": 52428800
            }
          ]
        }
      },
      "enterprise-tier": {
        "ClusterId": "enterprise-cluster",
        "Match": {
          "Path": "/api/enterprise/{**remainder}"
        },
        "Quote": {
          "ServerFundsRecipient": "GA3RQ7FWMT6INHS2R4KEKWENPYQOPLRNPYDAJFFRY5AUSD2GP6VG3OPY",
          "PricingStrategy": {
            "$type": "Basic",
            "Duration": 3600,
            "MicroUnitPrice": 300000
          },
          "RateLimiterStrategies": [
            {
              "$type": "CallCount",
              "Count": 1000
            },
            {
              "$type": "DataLimitDownload",
              "Bytes": 104857600
            }
          ]
        }
      }
    }
  }
}
Important: Each route must include a Quote section within the route definition for ApiCharge to properly associate ApiCharge Subscriptions with routes. The route ID (the key in the Routes object) is automatically used as the RouteId for the quote.

Best Practices

Route Design Guidelines

Common Pitfalls

Testing Routes

Use tools like curl or Postman to test your route configurations:

# Test path matching
curl -X GET https://your-apicharge-instance.com/api/users/123

# Test header-based routing  
curl -X GET -H "API-Version: 2.0" https://your-apicharge-instance.com/api/users

# Test query parameter routing
curl -X GET https://your-apicharge-instance.com/api/users?version=v1

# Test method-specific routing
curl -X POST -H "Content-Type: application/json" \
     -d '{"name":"test"}' \
     https://your-apicharge-instance.com/api/users

Next Steps

Now that you understand route matching, explore: