vercel/platforms

A full-stack Next.js app with multi-tenancy.

6,665 stars TypeScript 8 components

Routes subdomain requests to tenant-specific pages with Redis storage

Requests enter through middleware which extracts subdomain from hostname, determines routing destination, and either serves the main site with creation form, redirects to tenant-specific pages after Redis lookup, or provides admin interface for tenant management. Form submissions trigger server actions that validate input, store data in Redis, and redirect to newly created subdomains.

Under the hood, the system uses 2 feedback loops, 1 data pool, 4 control points to manage its runtime behavior.

A 8-component fullstack. 22 files analyzed. Data flows through 6 distinct pipeline stages.

How Data Flows Through the System

Requests enter through middleware which extracts subdomain from hostname, determines routing destination, and either serves the main site with creation form, redirects to tenant-specific pages after Redis lookup, or provides admin interface for tenant management. Form submissions trigger server actions that validate input, store data in Redis, and redirect to newly created subdomains.

  1. Extract subdomain from request — Middleware extracts subdomain from hostname using extractSubdomain function, handling different environments: localhost with pattern matching, production with domain suffix checking, and Vercel preview URLs with triple-dash parsing
  2. Route request based on subdomain — Middleware determines destination: if no subdomain detected, serves main site; if subdomain found, rewrites URL to /s/[subdomain] route; admin requests go to /admin unchanged
  3. Load tenant page data — The /s/[subdomain] page calls getSubdomainData which sanitizes the subdomain parameter and queries Redis using key `subdomain:{sanitizedSubdomain}` to retrieve tenant configuration
  4. Create new subdomain — createSubdomainAction validates form input (subdomain format must be lowercase alphanumeric with hyphens, emoji must pass isValidIcon check), stores SubdomainData in Redis, and redirects to new subdomain URL [CreateState → SubdomainData]
  5. List all tenants — getAllSubdomains scans Redis for keys matching pattern `subdomain:*`, retrieves all tenant data, and formats as Tenant array with subdomain names extracted from keys for admin dashboard display
  6. Delete tenant — deleteSubdomainAction removes the Redis key `subdomain:{subdomain}`, revalidates the admin page path to refresh the tenant list, and returns success/error feedback

Data Models

The data structures that flow between stages — the contracts that hold the system together.

SubdomainData lib/subdomains.ts
type with emoji: string (the tenant's chosen emoji icon) and createdAt: number (Unix timestamp)
Created when user submits subdomain form, stored in Redis with key `subdomain:{name}`, retrieved when subdomain is accessed or admin views dashboard
Tenant app/admin/dashboard.tsx
type with subdomain: string, emoji: string, and createdAt: number (flattened SubdomainData with subdomain key)
Constructed from Redis data by combining the key name with stored SubdomainData for admin dashboard display
CreateState app/subdomain-form.tsx
type with optional error: string, success: boolean, subdomain: string, and icon: string for form validation feedback
Returned by createSubdomainAction to communicate validation errors or success back to the form component
DeleteState app/admin/dashboard.tsx
type with optional error: string and success: string for deletion feedback
Returned by deleteSubdomainAction to show confirmation or error messages in admin interface

Hidden Assumptions

Things this code relies on but never validates. These are the things that cause silent failures when the system changes.

critical Environment unguarded

Environment variables KV_REST_API_URL and KV_REST_API_TOKEN are always present and valid when Redis client is instantiated

If this fails: Redis constructor will silently accept undefined values, causing cryptic connection errors or authentication failures only when first Redis operation is attempted

lib/redis.ts:Redis
critical Domain unguarded

Vercel preview URLs always follow the exact pattern 'tenant---branch-name.vercel.app' with triple dashes as the delimiter

If this fails: If Vercel changes their preview URL format or uses different delimiters, subdomain extraction fails and requests get routed to main site instead of tenant pages

middleware.ts:extractSubdomain
critical Contract unguarded

Redis set operation always succeeds when storing SubdomainData - no handling for Redis failures, memory limits, or network issues

If this fails: User sees success message and gets redirected to new subdomain URL, but tenant data was never stored, resulting in 404 page when they visit their supposedly created subdomain

app/actions.ts:createSubdomainAction
warning Temporal unguarded

Redis get operation to check for existing subdomain and subsequent set operation are atomic - no race condition handling

If this fails: Two users submitting the same subdomain simultaneously could both pass the duplicate check and overwrite each other's data, with the second user's emoji replacing the first user's

app/actions.ts:createSubdomainAction
critical Shape weakly guarded

Redis returns SubdomainData with exactly the expected shape {emoji: string, createdAt: number} - no validation of retrieved data structure

If this fails: If Redis contains corrupted data or data from a different schema version, the app will crash when trying to access .emoji or .createdAt properties on the returned object

lib/subdomains.ts:getSubdomainData
warning Scale unguarded

Redis SCAN operation will complete within reasonable time/memory limits even with large numbers of subdomain keys

If this fails: Admin dashboard becomes unusable when there are thousands of subdomains - page load times out or consumes excessive memory scanning all keys matching 'subdomain:*' pattern

lib/subdomains.ts:getAllSubdomains
warning Domain weakly guarded

Unicode property escapes (\p{Emoji}) are supported in the JavaScript runtime environment

If this fails: In older browsers or Node.js versions, the regex throws an error, falls back to length-only validation, and allows non-emoji text as icons, breaking the visual design

lib/subdomains.ts:isValidIcon
warning Environment weakly guarded

The 'host' header is always present and trustworthy in the NextRequest - no validation for missing or malformed host headers

If this fails: Malicious requests with missing or crafted host headers could cause substring operations to fail, middleware to crash, or subdomain extraction to return unexpected values

middleware.ts:extractSubdomain
info Contract unguarded

The params object from Next.js always contains a 'subdomain' key and getSubdomainData never throws exceptions

If this fails: If Next.js routing changes or Redis operations throw errors, metadata generation fails silently and pages render with generic titles instead of subdomain-specific ones

app/s/[subdomain]/page.tsx:generateMetadata
info Temporal unguarded

revalidatePath('/admin') successfully invalidates the cache and Redis DEL operation completes before the admin page re-renders

If this fails: Admin dashboard shows stale data with deleted tenants still visible if cache invalidation fails or Redis deletion is slow, confusing administrators

app/actions.ts:deleteSubdomainAction

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

Redis tenant store (database)
Key-value store using pattern `subdomain:{name}` where each key holds SubdomainData with emoji and creation timestamp

Feedback Loops

Delays

Control Points

Technology Stack

Next.js 15 (framework)
Full-stack React framework providing App Router, server actions, middleware, and deployment optimization
React 19 (framework)
UI library with hooks for state management and form handling in subdomain creation and admin interfaces
Upstash Redis (database)
Serverless Redis for tenant data persistence using key-value storage with async operations
Tailwind CSS (framework)
Utility-first CSS framework for component styling with custom design tokens and responsive design
Radix UI (library)
Unstyled, accessible UI primitives for dialogs, popovers, labels providing keyboard navigation and ARIA support
TypeScript (runtime)
Type system ensuring data model contracts between client and server components
Frimousse (library)
Emoji picker component library for tenant icon selection with search and categorization

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Fullstack Repositories

Frequently Asked Questions

What is platforms used for?

Routes subdomain requests to tenant-specific pages with Redis storage vercel/platforms is a 8-component fullstack written in TypeScript. Data flows through 6 distinct pipeline stages. The codebase contains 22 files.

How is platforms architected?

platforms is organized into 4 architecture layers: Request Routing, Tenant Management, Data Layer, UI Components. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through platforms?

Data moves through 6 stages: Extract subdomain from request → Route request based on subdomain → Load tenant page data → Create new subdomain → List all tenants → .... Requests enter through middleware which extracts subdomain from hostname, determines routing destination, and either serves the main site with creation form, redirects to tenant-specific pages after Redis lookup, or provides admin interface for tenant management. Form submissions trigger server actions that validate input, store data in Redis, and redirect to newly created subdomains. This pipeline design reflects a complex multi-stage processing system.

What technologies does platforms use?

The core stack includes Next.js 15 (Full-stack React framework providing App Router, server actions, middleware, and deployment optimization), React 19 (UI library with hooks for state management and form handling in subdomain creation and admin interfaces), Upstash Redis (Serverless Redis for tenant data persistence using key-value storage with async operations), Tailwind CSS (Utility-first CSS framework for component styling with custom design tokens and responsive design), Radix UI (Unstyled, accessible UI primitives for dialogs, popovers, labels providing keyboard navigation and ARIA support), TypeScript (Type system ensuring data model contracts between client and server components), and 1 more. A focused set of dependencies that keeps the build manageable.

What system dynamics does platforms have?

platforms exhibits 1 data pool (Redis tenant store), 2 feedback loops, 4 control points, 3 delays. The feedback loops handle self-correction and cache-invalidation. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does platforms use?

4 design patterns detected: Server Actions, Middleware Routing, Component Composition, Environment-Specific Logic.

Analyzed on April 20, 2026 by CodeSea. Written by .