jv_JACQUESVAN DEN BERG
CV
All projectsDEVELOPMENT JOURNAL / Hosted
Web development / VisionLink URL Shortener

A redirect service that never needs to render a page

A small redirect service with an important job in the VisionLink experience.

Open website Explore the stack
THE SHORT VERSION

What I built.

VisionLink URL Shortener is part of my portfolio of websites and applications.

  • Server-side short-slug resolution.
  • Reserved-route and asset-path handling.
  • Forwarding of destination query parameters.
TOOLS & RESPONSIBILITIES

Under the hood.

01

Nuxt / Nitro

Request handling before the Vue application

02

Supabase

Resolve a slug against digital_cards

03

TypeScript

URL and routing helpers

CONCEPTUAL IMPLEMENTATION FLOW
  1. Incoming slug
  2. Path guards
  3. Card lookup
  4. Query merge
  5. HTTP 302
BUILD NOTES / 01

One link at the start of the journey

Sharing a digital card should be straightforward. A short, readable address gives someone a clean way into the card without making the full application URL part of every conversation.

BUILD NOTES / 02

The request never reaches a component

The interesting file here is server/middleware/00-short-redirect.ts. It runs in Nitro, before the Vue application. The handler removes trailing slashes, skips framework and API prefixes, and accepts only one path segment. A dotted path is ignored too, which keeps a request for an asset from becoming a card lookup.

Only after those checks does it normalise the slug and query digital_cards for card_url. An empty destination redirects to the root. A resolved destination gets an HTTP 302. There is no loading spinner or client-side navigation step in this path.

BUILD NOTES / 03

The subtle part is the query string

The helper uses URLSearchParams to carry incoming parameters forward. If the target already contains a key, its value wins. That is a small policy decision with visible consequences: a shared link can carry extra context without overwriting parameters already configured on the card.

The response also sets Cache-Control to private, no-store. That favours a fresh destination over caching this redirect. It is a trade-off I want to explain explicitly; I have not published a benchmark that would justify calling the service fast by a particular number.

BUILD NOTES / 04

A small service with a narrow responsibility

For a redirect, the best interface is often no interface at all. I want the server to do the small amount of work required and send the visitor straight to the card. The management tools are a separate concern.

BUILD NOTES / 05

What I take from it

This is a compact example of putting a responsibility at the right layer. A small piece of server logic can remove unnecessary work from the public journey.

Request path · simplified from the implementation
if (isAppOrAssetPath(path)) return;
const slug = normalizeShortSlug(path);
const destination = await lookupCardUrl(slug);
setHeader(event, "Cache-Control", "private, no-store");
return sendRedirect(event, mergeQuery(destination) || "/", 302);

Explanatory pseudocode, shortened for readability.

WRITTEN BY JACQUES VAN DEN BERGNext build: Vaal Car Hire Website
↑ ↓ to navigateEnter to selectEsc to close