Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Route Matching

Route matching is the process by which ngen determines which conversion rules to apply to specific OpenAPI endpoints. This system uses regex pattern matching to provide flexible and powerful control over code generation.

Overview

Route matching works through a two-stage process:

  1. Pattern Matching: Each custom ruleset defines include_routes and exclude_routes patterns
  2. Priority Resolution: ngen determines which ruleset has the highest priority for each route

Pattern Syntax

Route patterns use standard regex syntax to match OpenAPI path strings. The matching is performed against the full path as it appears in the OpenAPI specification.

Basic Patterns

[api_routes]
include_routes = [
    "^/api/.*$",           # All routes starting with /api/
    "^/users/[0-9]+$",     # User routes with numeric IDs
    "/specific/route$"      # Exact match for specific route
]

Path Parameter Handling

OpenAPI path parameters (like {userId}) appear as literal text in the patterns:

[user_routes]
include_routes = [
    "^/users/\\{userId\\}/.*$",      # Routes under /users/{userId}/
    "^/rooms/\\{roomId\\}/events$"   # Specific parameterized route
]

Note: Curly braces {} are regex metacharacters, so they must be escaped with \\{ and \\} when you want to match them literally.

Common Pattern Examples

API Versioning

# Match all v1 routes
[api_v1]
include_routes = ["^/api/v1/.*$"]

# Match all v2 routes
[api_v2]
include_routes = ["^/api/v2/.*$"]

# Match multiple versions
[legacy_api]
include_routes = ["^/api/v[12]/.*$"]

Feature-Based Routing

# User management endpoints
[user_management]
include_routes = [
    "^/users/.*$",
    "^/auth/.*$",
    "^/profile/.*$"
]

# Admin endpoints
[admin_routes]
include_routes = ["^/admin/.*$"]
exclude_routes = ["^/admin/health$"]  # Exclude health check

Matrix Protocol Example

# Matrix Client-Server API v1
[matrix_client_v1]
include_routes = ["^/_matrix/client/v1/.*$"]
exclude_routes = [
    "^/_matrix/client/v1/admin/.*$",     # Admin routes handled separately
    "^/_matrix/client/v1/health$"        # Health check
]

# Matrix Server-Server API
[matrix_federation]
include_routes = ["^/_matrix/federation/.*$"]

Include vs Exclude Logic

The matching logic follows these rules:

  1. Include First: Route must match at least one include_routes pattern
  2. Exclude Override: If route matches any exclude_routes pattern, it’s excluded
  3. No Include = Match All: Empty include_routes array matches all routes

Truth Table

Include MatchExclude MatchResult
✅ Yes❌ No✅ Included
✅ Yes✅ Yes❌ Excluded
❌ No❌ No❌ Excluded
❌ No✅ Yes❌ Excluded

Example Behavior

[example_ruleset]
include_routes = ["^/api/.*$", "^/users/.*$"]
exclude_routes = ["^/api/internal/.*$", "^/users/admin/.*$"]

Given these patterns:

  • /api/posts → ✅ Included (matches include, no exclude match)
  • /api/internal/logs → ❌ Excluded (matches both, exclude wins)
  • /users/123 → ✅ Included (matches include, no exclude match)
  • /users/admin/settings → ❌ Excluded (matches both, exclude wins)
  • /health → ❌ Excluded (no include match)

Priority Resolution

When multiple rulesets could apply to the same route, ngen uses a priority system:

Priority Order (Highest to Lowest)

  1. Custom rulesets in configuration file order (first wins)
  2. General ruleset as fallback

Priority Example

[general]
convert = [{ from = "id", to = "String" }]
enabled = true

[specific_api]      # Higher priority (defined first)
include_routes = ["^/api/v2/.*$"]
convert = [{ from = "id", to = "ApiV2Id" }]

[generic_api]       # Lower priority (defined second)
include_routes = ["^/api/.*$"]
convert = [{ from = "id", to = "GenericId" }]

For route /api/v2/users:

  • Both specific_api and generic_api match
  • specific_api wins due to higher priority
  • id fields map to ApiV2Id

Advanced Matching Techniques

Negative Lookahead

Use regex negative lookahead to create complex exclusions:

[non_admin_routes]
include_routes = ["^/api/(?!admin).*$"]  # All /api/ routes except /api/admin/*

Multiple Route Groups

Combine multiple patterns for complex routing:

[content_api]
include_routes = [
    "^/posts/.*$",
    "^/comments/.*$",
    "^/media/.*$",
    "^/tags/.*$"
]
exclude_routes = [
    ".*/draft/.*",      # Exclude any draft routes
    ".*/internal/.*"    # Exclude any internal routes
]

Case Sensitivity

Regex patterns are case-sensitive by default. Use case-insensitive flags if needed:

[case_insensitive]
include_routes = ["(?i)^/API/.*$"]  # Matches /api/, /API/, /Api/, etc.

Debugging Route Matching

Testing Patterns

To test if your patterns work correctly:

  1. Use regex tools: Test patterns with online regex testers
  2. Check logs: ngen provides debug logging about route matching
  3. Start simple: Begin with broad patterns and refine

Common Mistakes

  1. Forgetting anchors: Use ^ and $ to match full paths

    # Wrong: matches any route containing "api"
    include_routes = ["api"]
    
    # Right: matches routes starting with /api/
    include_routes = ["^/api/.*$"]
    
  2. Unescaped special characters:

    # Wrong: braces are regex metacharacters
    include_routes = ["/users/{id}"]
    
    # Right: escape literal braces
    include_routes = ["/users/\\{id\\}"]
    
  3. Overly broad exclusions:

    # Dangerous: might exclude too much
    exclude_routes = [".*admin.*"]
    
    # Better: more specific pattern
    exclude_routes = ["^/admin/.*$", ".*/admin/.*$"]
    

Best Practices

  1. Order rulesets by specificity: Most specific first, most general last
  2. Use descriptive names: Make ruleset purposes clear
  3. Test thoroughly: Verify patterns match intended routes
  4. Document complex patterns: Add comments explaining regex logic
  5. Keep it simple: Prefer multiple simple patterns over one complex pattern
  6. Use anchors: Always use ^ and $ for precise matching