URL redirects — 404-path lookup

Exact-match redirect lookup (seo-listings card 21). Hot-path rule: call this ONLY from the storefront's not-found handler — never on regular page loads (index-covered exact match on (client_id, from_path)). When to_path is non-null, issue a 301 to it; when null, render the 404.

Redirects are created in admin and automatically on handle renames of products/collections/pages/posts — wiring this once means renames never break old URLs (path convention shared with content.md and menus.md).

GET /api/store/url-redirects?path=… — lookup

  • Purpose: resolve a missed pathname (e.g. /products/old-handle) to its redirect target, if any.
  • Auth: anon (x-client-id).
  • Request: query {path} — the exact pathname that 404'd (leading slash included), min length 1.
  • Response:
{
  "path": "/products/old-handle",   // echoed
  "to_path": "/products/new-handle" // or null — no redirect, render the 404
}
  • Errors: 400 validation_failed (missing/empty path) · 400 missing_client_id.
  • SDK: redirects.lookupRedirect(client, path)
  • Components: the storefront not-found handler (no visual component).
  • Settings: admin → Content → URL redirects; automatic rows on handle renames.
# Miss → to_path null (the everyday 404 case), echo intact.
BODY=$(curl -sf "$BASE/api/store/url-redirects?path=/doc-no-such-path-$RUN" \
  -H "x-client-id: $CLIENT_ID")
echo "$BODY" | grep -q '"to_path":null'
echo "$BODY" | grep -q "doc-no-such-path-$RUN"
# Missing path param → 400.
STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$BASE/api/store/url-redirects" \
  -H "x-client-id: $CLIENT_ID")
test "$STATUS" = 400

The hit case (to_path non-null) requires an admin-created redirect row; it is pinned executably by tests/store/url-redirects-lookup.test.ts and the SDK contract test tests/contract/sdk-content.contract.test.ts.