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:
- Pattern Matching: Each custom ruleset defines
include_routesandexclude_routespatterns - 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:
- Include First: Route must match at least one
include_routespattern - Exclude Override: If route matches any
exclude_routespattern, it’s excluded - No Include = Match All: Empty
include_routesarray matches all routes
Truth Table
| Include Match | Exclude Match | Result |
|---|---|---|
| ✅ 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)
- Custom rulesets in configuration file order (first wins)
- 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_apiandgeneric_apimatch specific_apiwins due to higher priorityidfields map toApiV2Id
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:
- Use regex tools: Test patterns with online regex testers
- Check logs: ngen provides debug logging about route matching
- Start simple: Begin with broad patterns and refine
Common Mistakes
-
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/.*$"] -
Unescaped special characters:
# Wrong: braces are regex metacharacters include_routes = ["/users/{id}"] # Right: escape literal braces include_routes = ["/users/\\{id\\}"] -
Overly broad exclusions:
# Dangerous: might exclude too much exclude_routes = [".*admin.*"] # Better: more specific pattern exclude_routes = ["^/admin/.*$", ".*/admin/.*$"]
Best Practices
- Order rulesets by specificity: Most specific first, most general last
- Use descriptive names: Make ruleset purposes clear
- Test thoroughly: Verify patterns match intended routes
- Document complex patterns: Add comments explaining regex logic
- Keep it simple: Prefer multiple simple patterns over one complex pattern
- Use anchors: Always use
^and$for precise matching