vercel/platforms
A full-stack Next.js app with multi-tenancy.
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.
- 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
- 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
- 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
- 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]
- 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
- 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.
lib/subdomains.tstype 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
app/admin/dashboard.tsxtype 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
app/subdomain-form.tsxtype 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
app/admin/dashboard.tsxtype 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.
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
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
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
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
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
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
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
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
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
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
Key-value store using pattern `subdomain:{name}` where each key holds SubdomainData with emoji and creation timestamp
Feedback Loops
- Form validation cycle (self-correction, balancing) — Trigger: User submits invalid subdomain or emoji. Action: createSubdomainAction returns error state, form re-renders with error message and preserves user input. Exit: Valid input submitted or user abandons form.
- Admin dashboard refresh (cache-invalidation, balancing) — Trigger: Tenant deletion action completes. Action: revalidatePath('/admin') forces Next.js to regenerate the admin page with updated tenant list. Exit: Page re-renders with current data.
Delays
- Redis network latency (async-processing, ~variable) — All tenant data operations wait for Redis responses - creation, lookup, listing, deletion
- Form submission processing (async-processing, ~brief) — Form shows loading state during server action execution and validation
- Page revalidation (cache-invalidation, ~brief) — Admin dashboard shows stale data until Next.js regenerates the page after revalidatePath call
Control Points
- KV_REST_API_URL (env-var) — Controls: Redis database connection endpoint for tenant data storage
- KV_REST_API_TOKEN (env-var) — Controls: Redis authentication token for database access
- NEXT_PUBLIC_ROOT_DOMAIN (env-var) — Controls: Base domain for subdomain routing and URL generation. Default: localhost:3000
- NODE_ENV (env-var) — Controls: Protocol selection (http for development, https for production) and middleware subdomain extraction logic
Technology Stack
Full-stack React framework providing App Router, server actions, middleware, and deployment optimization
UI library with hooks for state management and form handling in subdomain creation and admin interfaces
Serverless Redis for tenant data persistence using key-value storage with async operations
Utility-first CSS framework for component styling with custom design tokens and responsive design
Unstyled, accessible UI primitives for dialogs, popovers, labels providing keyboard navigation and ARIA support
Type system ensuring data model contracts between client and server components
Emoji picker component library for tenant icon selection with search and categorization
Key Components
- middleware (gateway) — Intercepts every HTTP request, extracts subdomain from hostname using environment-specific logic, and rewrites URLs to serve tenant pages or main site
middleware.ts - createSubdomainAction (processor) — Server action that validates subdomain format and emoji, sanitizes input, checks for duplicates, stores data in Redis, and redirects to new subdomain
app/actions.ts - redis (store) — Upstash Redis client configured with environment variables for tenant data persistence using key-value storage
lib/redis.ts - getSubdomainData (adapter) — Sanitizes subdomain input and retrieves tenant data from Redis using the `subdomain:{name}` key pattern
lib/subdomains.ts - isValidIcon (validator) — Validates that user input contains valid emoji characters within length limits using Unicode regex patterns with fallback validation
lib/subdomains.ts - extractSubdomain (resolver) — Parses hostname to detect subdomains across different environments - localhost development, production domains, and Vercel preview URLs with special handling for each
middleware.ts - SubdomainForm (processor) — React component with controlled form state for subdomain creation, emoji picker integration, and real-time validation feedback using useActionState
app/subdomain-form.tsx - AdminDashboard (orchestrator) — Manages the complete admin interface with tenant listing, deletion functionality, and state management for bulk operations
app/admin/dashboard.tsx
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated 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 Karolina Sarna.