Dub is an open-core link attribution platform for short links, conversion tracking, and affiliate programs, built as a Next.js and TypeScript monorepo on Prisma, PlanetScale, Upstash, and Tinybird.
- Language
- TypeScript
- Database
- PlanetScale
- Hosting
- Vercel
- Running for
- 2 years 4 months
- Team
- 2-5 people
Why this architecture
A Turborepo pnpm monorepo lets the Next.js dashboard, publishable SDKs, and embeddable widgets share code while each ships and versions independently, with click analytics offloaded to Tinybird to keep MySQL fast.
Tech stack by layer
8 technologies · audited Sep 17, 2026- Next.jsFull-stack React framework serving the dashboard, marketing site, and route handlers in apps/web.
- Tailwind CSSUtility-first styling shared across apps/web and @dub/ui through a shared @dub/tailwind-config preset.
- PrismaORM over the primary MySQL database, queried through the PlanetScale serverless driver adapter.
- NextAuth.jsSession and OAuth authentication for the dashboard, configured in apps/web.
- StripeBilling and subscription management for workspace plans.
- ResendTransactional email delivery for the @dub/email package, built on react-email templates.
- BoxyHQ SAML JacksonEnterprise SAML/SSO identity provider integration.
- PlanetScalePrimary MySQL-compatible database, accessed via @planetscale/database and the Prisma PlanetScale adapter.
- UpstashServerless Redis, rate limiting, vector search, and durable workflow queues (QStash) for the API layer.
- TinybirdClickHouse-backed analytics engine that ingests and serves link-click and event data.
Dub architecture diagram
Open SVGDiagram as text
- Dashboard (Next.js) → apps/web (Next.js on Vercel): HTTPS
- Short links (redirects) → apps/web (Next.js on Vercel): redirect
- Embeds (vanilla JS · React) → apps/web (Next.js on Vercel): API
- apps/web (Next.js on Vercel) → PlanetScale (MySQL · Prisma): Prisma
- apps/web (Next.js on Vercel) → Upstash (Redis · QStash · vector): cache · rate limit
- apps/web (Next.js on Vercel) → Tinybird (click analytics): click events
- apps/web (Next.js on Vercel) → Stripe (billing): subscriptions
- apps/web (Next.js on Vercel) → Resend (email): email
Key architectural decisions
6 decisions- 01
Turborepo pnpm monorepo separates the app from publishable packages
pnpm-workspace.yaml scopes the workspace to apps/*, apps/web/.react-email, packages/*, and packages/embeds/*; publishable SDKs (packages/cli, packages/ui, packages/utils, packages/embeds/core, packages/embeds/react) each ship to npm independently of the apps/web dashboard via their own publish-* scripts in the root package.json.
- 02
Prisma talks to MySQL through the PlanetScale serverless driver adapter
apps/web/package.json depends on @planetscale/database and @prisma/adapter-planetscale alongside @prisma/client, so Prisma queries route through PlanetScale's HTTP-based serverless driver instead of a persistent TCP connection, which matters for connection limits on serverless/edge deploys.
- 03
Click analytics are offloaded to Tinybird instead of the primary database
A dedicated packages/tinybird workspace package plus the @chronark/zod-bird client in apps/web/package.json route high-volume click and event data into Tinybird (a ClickHouse-based analytics engine) so analytics queries do not compete with the transactional MySQL workload.
- 04
Upstash covers cache, rate limiting, vector search, and workflows
apps/web/package.json depends on @upstash/redis, @upstash/ratelimit, @upstash/vector, and @upstash/workflow together, consolidating four infrastructure concerns (caching, API rate limiting, embedding search, durable background jobs) onto one serverless-Redis-based provider instead of separate self-hosted services.
- 05
Open core: AGPL-3.0 codebase with a separately licensed Enterprise Edition path
The root package.json declares "license": "AGPL-3.0-or-later" while GitHub reports the overall repository license as NOASSERTION; the README documents a commercially licensed "ee" (Enterprise Edition) code path under apps/web/app/(ee) for enterprise-only features maintained by the core team.
- 06
Embeds ship as a vanilla-JS core plus a thin React wrapper
packages/embeds/core (@dub/embed-core) implements the dashboard-embedding script with only @floating-ui/dom as a dependency, and packages/embeds/react (@dub/embed-react) wraps it in React components that depend on @dub/embed-core, so non-React consumers are not forced to ship React.
How Dub is built
How Dub is structured
Dub is a pnpm/Turborepo monorepo. pnpm-workspace.yaml scopes the workspace to apps/*, apps/web/.react-email, packages/*, and packages/embeds/*, and turbo.json defines the shared build, dev, test, and clean pipelines that turbo build / turbo dev run across them. The single application, apps/web, is the Next.js dashboard and API; everything else in packages/ is a separately publishable unit — packages/cli (the dub CLI, published via publish-cli), packages/ui (@dub/ui, shared React components), packages/utils (@dub/utils), packages/tailwind-config (@dub/tailwind-config), packages/email (@dub/email, React Email templates), packages/tinybird (analytics schema/queries), and packages/embeds/core plus packages/embeds/react (the embeddable dashboard widget). Two integration packages, packages/hubspot-app and packages/stripe-app, ship platform-specific extensions for HubSpot and Stripe App Marketplace rather than being consumed by apps/web directly.
Frontend
apps/web runs on Next.js 15 with React 19, per the pinned next and react versions in apps/web/package.json. Styling is Tailwind CSS 3.x, configured through the shared @dub/tailwind-config preset in packages/tailwind-config so the dashboard and the @dub/ui component library stay visually consistent. UI primitives layer Radix UI (@radix-ui/react-hover-card), cmdk for command palettes, vaul for drawers, and @visx/* packages for charts; shiki renders syntax-highlighted code blocks and tiptap powers rich-text editing.
Backend & APIs
API route handlers live inside apps/web (Next.js route handlers), backed by Prisma as the ORM. Authentication runs through next-auth (v4, apps/web/package.json) for standard OAuth/session login, with @boxyhq/saml-jackson bolted on for enterprise SAML/SSO. Billing is handled by the stripe SDK, and outbound email goes through the separate @dub/email package (packages/email/package.json), which wraps Resend and react-email templates rather than sending mail directly from apps/web. AI-assisted features (e.g. content generation, chat) use the Vercel AI SDK (ai, @ai-sdk/anthropic, @ai-sdk/react, @ai-sdk/rsc) and @mendable/firecrawl-js for web scraping.
Data & persistence
The system of record is MySQL, hosted on PlanetScale and accessed through Prisma using the @prisma/adapter-planetscale driver adapter on top of @planetscale/database — a serverless, HTTP-based MySQL driver rather than a long-lived TCP connection, which matters for connection-pooling under serverless/edge execution. High-volume analytics (link clicks, events) do not go through MySQL at all: a dedicated packages/tinybird package and the @chronark/zod-bird client in apps/web/package.json route that traffic into Tinybird, a managed ClickHouse-based analytics engine, keeping analytics ingestion and querying off the transactional database. Upstash provides the remaining data infrastructure in one vendor: @upstash/redis for caching, @upstash/ratelimit for API rate limiting, @upstash/vector for embedding/vector search, and @upstash/workflow (QStash-backed) for durable background jobs.
Build, test & deploy
turbo build, turbo dev, and turbo run test drive the workspace from the root package.json; apps/web's own build script runs pnpm prisma:generate before next build. Tests run through Vitest (vitest -no-file-parallelism --bail=1 for unit tests) and Playwright for end-to-end coverage (test:e2e, test:e2e:ui), with dedicated GitHub Actions workflows for e2e.yaml and playwright.yaml, plus prettier.yaml for formatting checks. apps/web/package.json depends on @vercel/edge-config, @vercel/functions, and @vercel/og, indicating the app is built to run on Vercel's edge/functions runtime and use Vercel-native OG image generation, even though no vercel.json was present in the audited tree.
Self-hosting notes
The README includes a "Self-Hosting" section pointing to https://dub.co/docs/self-hosting/guide and documents recommended node (v23.11.0) and pnpm (9.15.9) versions plus common local-development fixes (missing Prisma tables via pnpm prisma:push, stale node_modules/.next/.turbo caches). A pnpm run script dev/seed command (run from apps/web) seeds development data, with a --truncate flag to reset the database first.
What to copy (and what not to)
The split between one Next.js application and several independently versioned, independently publishable packages (@dub/ui, @dub/utils, @dub/embed-core/@dub/embed-react) is a solid pattern for a product that needs both a hosted dashboard and an embeddable/SDK surface. Routing high-cardinality analytics data to a purpose-built engine (Tinybird/ClickHouse) instead of the transactional database, and consolidating cache/rate-limit/vector/queue needs onto a single provider (Upstash), are both reasonable defaults to borrow. What is more specific to Dub's scale and commercial model is the open-core split itself — a separately licensed ee directory gated inside the same Next.js app — which adds real build and access-control complexity that a purely open-source project would not need.
Sources & repo audit
- Audited
- Sep 17, 2026
- Commit
- 82767f9
- License
- NOASSERTION
- apps/web/package.json (Next.js, Prisma, PlanetScale, Upstash, Stripe, NextAuth.js)
- pnpm-workspace.yaml
- turbo.json
- packages/embeds/core/package.json
- packages/email/package.json (Resend, react-email)
- README.md (open-core license model, self-hosting notes)
Independent analysis of repository at github.com/dubinc/dub. Spotted an inaccuracy? Use the claim form to request a correction.
Show this stack on your README or website
[](https://stackitfast.com/project/dub)Paste it into your README. It shows your current stack and links to this page.
Scaffold it with your agent
Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with Dub's architecture.
- 1Copy the promptThe full markdown spec, with every layer and decision.
- 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
- 3Paste and scaffoldUse it as the first instruction; review before you ship.
# MISSION: Scaffold "Dub" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **Dub**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Dub
- **What It Does**: Dub is an open-core link attribution platform for short links, conversion tracking, and affiliate programs, built as a Next.js and TypeScript monorepo on Prisma, PlanetScale, Upstash, and Tinybird.
- **Domain & Category**: Link & Analytics SaaS
- **Production Scale**: 2-5 people
- **Development Mode**: HYBRID
- **Architectural Rationale**: A Turborepo pnpm monorepo lets the Next.js dashboard, publishable SDKs, and embeddable widgets share code while each ships and versions independently, with click analytics offloaded to Tinybird to keep MySQL fast.
- **Live Website Reference**: https://dub.co
- **Source Repository**: https://github.com/dubinc/dub
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: Next.js, TypeScript, Prisma, PlanetScale, Upstash, Tinybird, Tailwind CSS, Turborepo
- **Primary Language(s)**: TypeScript, JavaScript, CSS
- **License of the reference repo**: NOASSERTION
- **Frontend**: Next.js — Full-stack React framework serving the dashboard, marketing site, and route handlers in apps/web.; Tailwind CSS — Utility-first styling shared across apps/web and @dub/ui through a shared @dub/tailwind-config preset.
- **Backend & APIs**: Prisma — ORM over the primary MySQL database, queried through the PlanetScale serverless driver adapter.; NextAuth.js — Session and OAuth authentication for the dashboard, configured in apps/web.; Stripe — Billing and subscription management for workspace plans.; Resend — Transactional email delivery for the @dub/email package, built on react-email templates.; BoxyHQ SAML Jackson — Enterprise SAML/SSO identity provider integration.
- **Data & persistence**: PlanetScale — Primary MySQL-compatible database, accessed via @planetscale/database and the Prisma PlanetScale adapter.; Upstash — Serverless Redis, rate limiting, vector search, and durable workflow queues (QStash) for the API layer.; Tinybird — ClickHouse-backed analytics engine that ingests and serves link-click and event data.
- **Infrastructure & deploy**: Turborepo — Orchestrates build, dev, and test pipelines across the pnpm workspace of apps and packages.; Vercel — Hosting platform; apps/web uses Vercel-specific runtime primitives for edge config, functions, and OG image generation.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/dubinc/dub @ 82767f9)
1. **Turborepo pnpm monorepo separates the app from publishable packages**: pnpm-workspace.yaml scopes the workspace to apps/*, apps/web/.react-email, packages/*, and packages/embeds/*; publishable SDKs (packages/cli, packages/ui, packages/utils, packages/embeds/core, packages/embeds/react) each ship to npm independently of the apps/web dashboard via their own publish-* scripts in the root package.json.
2. **Prisma talks to MySQL through the PlanetScale serverless driver adapter**: apps/web/package.json depends on @planetscale/database and @prisma/adapter-planetscale alongside @prisma/client, so Prisma queries route through PlanetScale's HTTP-based serverless driver instead of a persistent TCP connection, which matters for connection limits on serverless/edge deploys.
3. **Click analytics are offloaded to Tinybird instead of the primary database**: A dedicated packages/tinybird workspace package plus the @chronark/zod-bird client in apps/web/package.json route high-volume click and event data into Tinybird (a ClickHouse-based analytics engine) so analytics queries do not compete with the transactional MySQL workload.
4. **Upstash covers cache, rate limiting, vector search, and workflows**: apps/web/package.json depends on @upstash/redis, @upstash/ratelimit, @upstash/vector, and @upstash/workflow together, consolidating four infrastructure concerns (caching, API rate limiting, embedding search, durable background jobs) onto one serverless-Redis-based provider instead of separate self-hosted services.
5. **Open core: AGPL-3.0 codebase with a separately licensed Enterprise Edition path**: The root package.json declares "license": "AGPL-3.0-or-later" while GitHub reports the overall repository license as NOASSERTION; the README documents a commercially licensed "ee" (Enterprise Edition) code path under apps/web/app/(ee) for enterprise-only features maintained by the core team.
6. **Embeds ship as a vanilla-JS core plus a thin React wrapper**: packages/embeds/core (@dub/embed-core) implements the dashboard-embedding script with only @floating-ui/dom as a dependency, and packages/embeds/react (@dub/embed-react) wraps it in React components that depend on @dub/embed-core, so non-React consumers are not forced to ship React.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
- Structure as a Turborepo monorepo with strict package boundaries:
- `apps/web`: Application UI, routing, layouts, and server endpoints.
- `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
- `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
- `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
- Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
- Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
- Utilize Next.js 15 App Router: default to React Server Components (RSC) for data fetching. Handle mutations through Server Actions with Zod validation. Keep 'use client' directives restricted strictly to interactive leaf components.
4. **Design System & Aesthetics**:
- Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
- Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
- Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
- Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (PlanetScale, Upstash, Tinybird): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.Copied 1 time
Frequently asked about Dub
What is Dub built with?
Dub is a Next.js and TypeScript application using Prisma over a PlanetScale MySQL database, Upstash for Redis/rate-limiting/vector search, Tinybird for click analytics, and Turborepo to manage the pnpm monorepo.
What database does Dub use?
The primary datastore is a MySQL-compatible PlanetScale database, queried through Prisma using the @prisma/adapter-planetscale serverless driver adapter declared in apps/web/package.json.
How does Dub handle link-click analytics?
Click and event analytics are routed into Tinybird, a ClickHouse-based analytics engine, through a dedicated packages/tinybird workspace package and the @chronark/zod-bird client, keeping analytics traffic off the primary MySQL database.
Can I self-host Dub?
The README documents a self-hosting guide; the core dub monorepo (apps/web plus the workspace packages) is AGPL-3.0-or-later licensed per its package.json, while a separate "ee" (Enterprise Edition) path under apps/web/app/(ee) is commercially licensed.
Does Dub support enterprise SSO?
Yes. apps/web/package.json depends on @boxyhq/saml-jackson for SAML/SSO identity provider integration alongside NextAuth.js for standard session and OAuth authentication.
One email a month: new deep dives and stack trends
New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.
Similar architectures
- PapermarkAI-agentDocument Analytics SaaS · SoloShares TypeScript · Next.js · Prisma
- UmamiHybridWeb Analytics · SoloShares TypeScript · Next.js · Prisma
- FormbricksAI-agentSurvey & Feedback SaaS · 2-5 peopleShares Next.js · TypeScript · Prisma
- TaxonomyAI-agentOpen-Source SaaS Template · 1-5 peopleShares Next.js · TypeScript · Prisma
- Cal.comClassicScheduling SaaS · 6-20 peopleShares TypeScript · Next.js · Prisma
- shadcn/uiHybridCopy-Paste UI Component Architecture · 1M+ MAUShares Tailwind CSS · TypeScript · Turborepo