This document covers the route identification, resolution logic, and route type handling in the VTEX Rewriter application.
📋 Table of Contents
- Route Identification Flow
- Route Resolution Types
- Internal Route Resolution
- Redirect Processing
- Compatibility Handling
🎯 Route Identification Flow
Identification Algorithm
_25async function identifyRoute(ctx: Context) {_25 const { url, binding } = ctx.state_25 const locator = { from: url.pathname, binding: binding.id }_25 _25 // 1. Search redirect (highest priority)_25 const redirect = await findRedirect(locator, url.search)_25 if (redirect) {_25 return createRedirectRoute(redirect, url)_25 }_25 _25 // 2. Search internal route_25 const internal = await findInternal(locator)_25 if (internal) {_25 return createInternalRoute(internal, url)_25 }_25 _25 // 3. Try compatibility with legacy URLs_25 const compatibility = await findCompatibilityRoute(locator)_25 if (compatibility) {_25 return createCompatibilityRoute(compatibility, url)_25 }_25 _25 // 4. Fallback to search or 404_25 return createFallbackRoute(url)_25}
Route Identification Detailed Flow
🔀 Route Resolution Types
Redirect Search
_13async function findRedirect(locator: RouteLocator, search?: string) {_13 // First try with complete query string_13 if (search) {_13 const withQS = await redirects.get({_13 ...locator,_13 from: `${locator.from}${search}`_13 })_13 if (withQS) return withQS_13 }_13 _13 // Then try without query string_13 return redirects.get(locator)_13}
Internal Route Search
_11async function findInternal(locator: RouteLocator) {_11 // Direct search in index_11 const direct = await internals.get(locator)_11 if (direct && direct.type !== 'notFoundProduct') {_11 return { route: direct, fromIndex: true }_11 }_11 _11 // Search by compatibility (legacy URLs)_11 const compatibility = await findCompatibilityRoute(locator)_11 return { route: compatibility, fromIndex: false }_11}
🎯 Internal Route Resolution
1. Redirect Resolution
_17function resolveRedirect(redirect: Redirect, url: ParsedUrl): RedirectRoute {_17 return {_17 type: 'redirect',_17 status: redirect.type === 'PERMANENT' ? 301 : 302,_17 location: buildRedirectLocation(redirect.to, url.query),_17 from: redirect.from_17 }_17}_17_17function buildRedirectLocation(to: string, query?: QueryParams): string {_17 // Preserve query parameters if not specified in destination_17 if (query && !to.includes('?')) {_17 const queryString = new URLSearchParams(query).toString()_17 return queryString ? `${to}?${queryString}` : to_17 }_17 return to_17}
2. Internal Route Resolution
_16async function resolveInternal(internal: Internal, url: ParsedUrl): Promise<InternalRoute> {_16 const resolver = getRouteResolver(internal.type)_16 const href = await resolver.resolve(internal, url.query)_16 _16 return {_16 type: 'internal',_16 href: href,_16 from: internal.from,_16 binding: internal.binding,_16 meta: {_16 declarer: internal.declarer,_16 entityId: internal.id,_16 routeType: internal.type_16 }_16 }_16}
3. Route Resolvers by Type
Product Route Resolver
_28class ProductRouteResolver {_28 async resolve(route: Internal, query: QueryParams): Promise<string> {_28 const { id, binding } = route_28 _28 // Fetch product data from catalog_28 const product = await catalog.getProduct(id)_28 if (!product) {_28 throw new Error('Product not found')_28 }_28 _28 // Build final URL_28 const basePath = `/produto/${product.linkText}/p`_28 const queryString = this.buildQueryString(query)_28 _28 return `${basePath}${queryString}`_28 }_28 _28 private buildQueryString(query: QueryParams): string {_28 const params = new URLSearchParams()_28 _28 // Preserve important parameters_28 if (query.skuId) params.set('skuId', query.skuId)_28 if (query.utm_source) params.set('utm_source', query.utm_source)_28 _28 const result = params.toString()_28 return result ? `?${result}` : ''_28 }_28}
Category Route Resolver
_14class CategoryRouteResolver {_14 async resolve(route: Internal, query: QueryParams): Promise<string> {_14 const { id } = route_14 _14 // Fetch category tree_14 const category = await catalog.getCategory(id)_14 const categoryTree = await catalog.getCategoryTree(id)_14 _14 // Build hierarchical path_14 const path = categoryTree.map(c => c.linkText).join('/')_14 _14 return `/categoria/${path}`_14 }_14}
🔀 Redirect Processing Flow
🔄 Compatibility Handling
The compatibility layer handles legacy URL patterns and transforms them to current routing standards.
Legacy Pattern Detection
_18// Legacy patterns are detected through regex matching and catalog API lookups_18async function findCompatibilityRoute(locator: RouteLocator) {_18 // Check various legacy patterns_18 const patterns = [_18 productLegacyPattern,_18 categoryLegacyPattern,_18 brandLegacyPattern_18 ]_18 _18 for (const pattern of patterns) {_18 const match = await pattern.test(locator.from)_18 if (match) {_18 return await pattern.resolve(match, locator.binding)_18 }_18 }_18 _18 return null_18}
🚀 Auto-Cache Integration
The route resolution system now includes an auto-cache mechanism that automatically saves compatibility routes to VBase, improving future performance.
Auto-Cache in Route Resolution
_68async function findInternal(locator: RouteLocator, workspace: string) {_68 // 1. Try direct VBase lookup first (fast)_68 const vbaseStart = Date.now()_68 const direct = await internals.get(locator)_68 const vbaseElapsed = Date.now() - vbaseStart_68 _68 if (direct && direct.type !== 'notFoundProduct') {_68 // Cache hit - log performance metrics_68 metrics.batch('route-performance-tracking', undefined, {_68 route_path: locator.from,_68 access_type: 'cache_hit',_68 latency_ms: vbaseElapsed,_68 vbase_ms: vbaseElapsed,_68 compatibility_ms: 0,_68 total: 1,_68 })_68 return { route: direct, fromIndex: true }_68 }_68_68 // 2. Cache miss - try compatibility layer (slower)_68 const compatStart = Date.now()_68 const compatibility = await findCompatibilityRoute(locator)_68 const compatElapsed = Date.now() - compatStart_68 _68 if (compatibility) {_68 const totalLatency = vbaseElapsed + compatElapsed_68 _68 // Log cache miss metrics_68 metrics.batch('route-performance-tracking', undefined, {_68 route_path: locator.from,_68 access_type: 'cache_miss_first_time',_68 latency_ms: totalLatency,_68 vbase_ms: vbaseElapsed,_68 compatibility_ms: compatElapsed,_68 will_be_cached: 1,_68 total: 1,_68 })_68_68 // Auto-cache for production only_68 if (workspace === 'master' && compatibility.type !== 'notFoundProduct') {_68 saveCompatibilityRouteInBackground(compatibility, internals, redirects)_68 }_68 }_68 _68 return { route: compatibility, fromIndex: false }_68}_68_68// Background save function (non-blocking)_68function saveCompatibilityRouteInBackground(_68 compatibility: Internal,_68 internals: any,_68 redirects: any_68) {_68 const tomorrow = new Date()_68 tomorrow.setDate(tomorrow.getDate() + 1)_68 _68 const routeWithExpiry = {_68 ...compatibility,_68 endDate: tomorrow.toISOString(), // 24h TTL_68 }_68 _68 // Fire-and-forget async save_68 setImmediate(() => {_68 internals.save(routeWithExpiry, redirects).catch(() => {_68 // Silent error handling - don't impact user response_68 })_68 })_68}
Performance Evolution Example
_11Request 1: /produto/smartphone/p_11├── VBase lookup: 15ms (miss)_11├── Compatibility check: 520ms (Catalog API)_11├── Total: 535ms_11└── 🚀 Auto-save to VBase (background)_11_11Request 2: /produto/smartphone/p (same route)_11├── VBase lookup: 10ms (hit!)_11├── Compatibility check: 0ms (skipped)_11├── Total: 10ms_11└── ✨ 98% improvement!
This auto-cache system transforms the rewriter from a 20% cache hit rate to 95%+ over time, with each compatibility route lookup automatically becoming a fast VBase hit for future requests.
_10