{
  "license": "CC BY 4.0",
  "recipe": {
    "slug": "spa-navigation-latency-by-route",
    "title": "Measure SPA Navigation Latency by Route",
    "testedWith": "Apache DataFusion 45.2.0",
    "lastReviewed": "2026-07-28"
  },
  "contract": {
    "tableName": "frontend_navigation_events",
    "schema": [
      {
        "name": "timestamp_utc",
        "type": "Timestamp",
        "description": "Navigation completion time."
      },
      {
        "name": "destination_route",
        "type": "Utf8",
        "description": "Stable destination route template."
      },
      {
        "name": "release",
        "type": "Utf8",
        "description": "Frontend release identifier."
      },
      {
        "name": "duration_ms",
        "type": "Float64",
        "description": "Time from navigation start to ready state."
      },
      {
        "name": "status",
        "type": "Utf8",
        "description": "success, failed, or abandoned."
      }
    ]
  },
  "query": "SELECT\n  destination_route,\n  release,\n  COUNT(*) AS navigations,\n  approx_percentile_cont(duration_ms, 0.50) AS p50_ms,\n  approx_percentile_cont(duration_ms, 0.95) AS p95_ms,\n  100.0 * SUM(CASE WHEN status <> 'success' THEN 1 ELSE 0 END)\n    / NULLIF(COUNT(*), 0) AS unsuccessful_pct\nFROM frontend_navigation_events\nWHERE timestamp_utc >= now() - INTERVAL '7 days'\nGROUP BY destination_route, release\nHAVING COUNT(*) >= 50\nORDER BY p95_ms DESC;",
  "fixtureKind": "schema-example",
  "inputRows": [
    {
      "timestamp_utc": "2026-07-28T10:00:00Z",
      "destination_route": "/api/checkout",
      "release": "release_2026_07_1",
      "duration_ms": 12,
      "status": "success"
    },
    {
      "timestamp_utc": "2026-07-28T11:00:00Z",
      "destination_route": "/api/search",
      "release": "release_2026_07_2",
      "duration_ms": 37,
      "status": "failed"
    },
    {
      "timestamp_utc": "2026-07-28T12:00:00Z",
      "destination_route": "/api/projects/:id",
      "release": "release_2026_07_3",
      "duration_ms": 24,
      "status": "success"
    }
  ],
  "illustrativeInputRows": [
    {
      "timestamp_utc": "2026-07-28T10:00:00Z",
      "destination_route": "/api/checkout",
      "release": "release_2026_07_1",
      "duration_ms": 12,
      "status": "success"
    },
    {
      "timestamp_utc": "2026-07-28T11:00:00Z",
      "destination_route": "/api/search",
      "release": "release_2026_07_2",
      "duration_ms": 37,
      "status": "failed"
    },
    {
      "timestamp_utc": "2026-07-28T12:00:00Z",
      "destination_route": "/api/projects/:id",
      "release": "release_2026_07_3",
      "duration_ms": 24,
      "status": "success"
    }
  ],
  "expectedOutput": {
    "columns": [
      "destination_route",
      "release",
      "navigations",
      "p50_ms",
      "p95_ms",
      "unsuccessful_pct"
    ],
    "rows": [
      {
        "destination_route": "/reports/:id",
        "release": "web-103",
        "navigations": 640,
        "p50_ms": 780,
        "p95_ms": 3210,
        "unsuccessful_pct": 3.13
      },
      {
        "destination_route": "/projects/:id",
        "release": "web-103",
        "navigations": 1820,
        "p50_ms": 410,
        "p95_ms": 1480,
        "unsuccessful_pct": 1.21
      }
    ]
  },
  "notes": [
    "The compact input rows demonstrate field names and JSON types; they are not claimed to generate the displayed aggregate by themselves.",
    "The expected output is deterministic synthetic review data for the documented output shape.",
    "Adapt time windows, thresholds, identities, and business definitions before operational use."
  ]
}