Browse docs

Architecture

For developers and operators self-hosting Linktrap — how the app, database, auth, and billing connect.

Day-to-day link creation does not require reading this page. If you only use the website, start with Getting started.

Runtime stack

  • Framework: Next.js 16 App Router (React 19), Tailwind 4.
  • Database: Turso (libSQL) via @libsql/client + Drizzle ORM (db/schema.ts).
  • Auth: Clerk (middleware.ts + session on API routes).
  • Billing: Stripe Checkout, Customer Portal, webhooks.
  • Optional: OpenAI + Vercel Blob for AI-generated preview text/images in the wizard.

Request flows

Marketing & app UI

  • / — Server-rendered landing.
  • /create — Client wizard (dynamic import, no SSR); requires sign-in via middleware.
  • /dashboard — Workspace links and Pro tooling.

Short link resolution

GET /s/[slug] loads the link row, checks isActive and expiresAt, classifies User-Agent via lib/crawler-uas.ts:

  • Preview bot: returns HTML with og:title, og:description, optional og:image, Vary: User-Agent.
  • Human: 302 to destination with UTM query params merged in; click logged asynchronously to link_clicks.

Middleware

Clerk protects private routes. Optional rewrite: verified custom host + path /slug → internal /s/slug (see Custom domains).

Data model (conceptual)

  • users — maps Clerk user id to internal id.
  • workspaces — plan (free / pro), Stripe customer/subscription fields.
  • workspace_members — user ↔ workspace (owner role today).
  • links — slug, destination, bot meta, workspace, UTM, expiry, active flag.
  • link_clicks — per-click analytics (referrer, country).
  • api_keys — hashed keys for /api/v1 (Pro).
  • custom_domains — hostname → workspace (verified flag).
  • stripe_events_processed — webhook idempotency.
  • rate_limit_buckets — per-fingerprint minute windows.

Details: Database schema.