Skip to Content
Core ConceptsRoute Resolution

Route Resolution

This page explains how URLs are resolved to content, including context handling, alias resolution, and optional segments.

Resolution Flow Overview

When a request comes in for a URL like /uk/en/jackets/parka, Foir resolves it through several steps:

Request: GET /uk/en/jackets/parka ┌────────────────────────────────────┐ │ 1. Load Route Tree (cached) │ │ Pattern: /:country/:locale/... │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 2. Match Path to Expectations │ │ uk → country context │ │ en → locale context │ │ jackets → collection entity │ │ parka → product entity │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 3. Batch Fetch Entities │ │ Single query for all 4 │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 4. Validate Memberships │ │ Is parka in jackets? │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 5. Return Resolved Content │ │ Entity + contexts + parents │ └────────────────────────────────────┘

Context Entities

Context entities are special entity types used for URL prefixes like markets, countries, and locales.

Context Configuration

Each context is configured with:

PropertyDescriptionExample
entityModelKeyEntity model for context valueslocale, country
canonicalFieldField for canonical URL valuenaturalKey
defaultRecordKeyDefault when omitted from URLen for locale
validValuesAll valid context values + aliases{en, fr, de, gb, uk, ...}

Valid Values Loading

When the route tree is cached, valid values for each context are loaded:

Context: locale ├── Natural Keys: en, fr, de, es, ... └── Aliases: english → en, french → fr, ... Context: country ├── Natural Keys: gb, us, de, fr, ... └── Aliases: uk → gb, britain → gb, ...

This allows the router to distinguish between context segments and entity segments.

Alias Resolution

Entities can have aliases - alternative URLs that resolve to the same content.

Alias Configuration

// Country entity: 'gb' { naturalKey: 'gb', alias: ['uk', 'britain'], aliasStrategy: 'redirect' // or 'canonical-tag' }

Alias Strategies

StrategyBehaviorUse Case
redirect301 redirect to canonical URLSEO - consolidate link equity
canonical-tagServe with <link rel="canonical">A/B testing, soft migration

Resolution Example

Request: /uk/en/jackets/parka ┌────────────────────────────────────┐ │ Is 'uk' a valid country? │ │ → YES (alias for 'gb') │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ aliasStrategy = 'redirect' │ │ → 301 to /gb/en/jackets/parka │ └────────────────────────────────────┘

Optional Context Segments

Context segments can be optional when a default value is configured.

How It Works

If a context has defaultRecordKey set, the segment can be omitted from the URL:

Route Tree: /:country/:locale/:collection/:product With defaults: - country.defaultRecordKey = 'gb' - locale.defaultRecordKey = 'en' Request: /jackets/parka ┌────────────────────────────────────┐ │ 'jackets' not a valid country │ │ → Use default 'gb' │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 'jackets' not a valid locale │ │ → Use default 'en' │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 'jackets' matches collection │ │ 'parka' matches product │ └────────────────────────────────────┘ Resolved: /gb/en/jackets/parka (with defaults applied)

Supported URL Patterns

URLResolved As
/gb/en/jackets/parkaExplicit country and locale
/gb/jackets/parkaExplicit country, default locale
/jackets/parkaDefault country and locale
/en/jackets/parkaDefault country, explicit locale (if ‘en’ not a country)

Membership Validation

After fetching entities, memberships are validated to ensure the URL is valid.

Parent Memberships

Products belong to collections via parentMemberships:

// Product: parka { parentMemberships: { 'shopify-collection': ['jackets-id', 'outerwear-id', 'sale-id'] } }

For /jackets/parka to resolve, parka must have jackets in its parent memberships.

Validation Flow

Target: parka (product) Check 1: Is 'jackets' in parka.parentMemberships['collection']? → YES ✓ All checks pass → Route resolves successfully

Performance

Route resolution is optimized for speed:

StepTypical Time
Route tree cache hit0-1ms
Path matching1-2ms
Batch entity fetch10-20ms
Membership validation1-2ms
Total (uncached)~30ms
Total (cached)~5-10ms

Caching

  • Route tree: Cached per-project, 5 minute TTL
  • Context valid values: Loaded with route tree
  • Entity lookups: Batched in single query

Next Steps

Last updated on