From Zero to Production in 10 Minutes
From Zero to Production in 10 Minutes
Build a complete business application with auth, content, and payments — faster than you can order lunch.
RevealUI is an agentic business runtime. Instead of gluing together a dozen SaaS tools and spending weeks on boilerplate, you get users, content, products, payments, and intelligence pre-wired and ready to deploy.
This tutorial walks you through creating a real business application from scratch. By the end, you will have a working CMS with typed collections, session-based authentication, a REST API with Swagger documentation, Stripe billing, license enforcement, and an admin dashboard — deployed to production on Vercel.
Let's start the clock.
Prerequisites
Before you begin, make sure you have the following installed and ready:
- Node.js 24+ — check with
node --version - pnpm 10+ — check with
pnpm --version(install withcorepack enable && corepack prepare pnpm@latest --activate) - A Stripe account — test mode is fine for this tutorial
- A NeonDB account — the free tier works perfectly
Tip: If you already have Node.js but not pnpm, the fastest path is
corepack enable— it ships with Node.js and activates pnpm instantly.
Step 1: Scaffold your project
Run the create-revealui initializer:
npx create-revealui@latest my-business
The CLI walks you through an 8-step setup wizard:
@revealui/cli [1/8] Validating Node.js version...
@revealui/cli Node.js version: v24.13.0
@revealui/cli [2/8] Configure your project
? What is your project name? my-business
? Which template would you like to use?
> Basic Blog - A simple blog with posts and pages
E-commerce - Product catalog with checkout
Portfolio - Personal portfolio site
Select Basic Blog for this tutorial. It comes with a Posts collection, sample content, and a clean starting point.
The wizard continues through database, storage, payment, and dev environment setup:
@revealui/cli [3/8] Configure database
? Which database provider would you like to use?
> NeonDB - Serverless PostgreSQL (recommended)
Supabase - PostgreSQL with built-in features
Local PostgreSQL - Use existing local database
Skip - Configure later
@revealui/cli [4/8] Configure storage
? Which storage provider would you like to use?
> Vercel Blob - Simple object storage (recommended)
Supabase Storage - Integrated with Supabase
Skip - Configure later
@revealui/cli [5/8] Configure payments
? Do you want to configure Stripe payments? Yes
@revealui/cli [6/8] Configure development environment
@revealui/cli Dev Container: No, Devbox: No
@revealui/cli [7/8] Creating project...
@revealui/cli Project created successfully
@revealui/cli [8/8] Next steps
─────────────────────────────────
cd my-business
pnpm install
pnpm dev
─────────────────────────────────
Now enter your project:
cd my-business
Tip: You can skip all prompts and use defaults with
npx create-revealui@latest my-business -y. You can also pre-select a template:npx create-revealui@latest my-business --template e-commerce.
Step 2: Configure your environment
The CLI generates a .env.development.local file with your credentials already filled in (if you provided them during setup). If you chose "Skip" for any step, or want to configure things manually, here is the full structure:
# .env.development.local
# =============================================================================
# REQUIRED - REVEALUI CORE
# =============================================================================
REVEALUI_SECRET=<auto-generated-64-char-hex-string>
REVEALUI_PUBLIC_SERVER_URL=http://localhost:4000
NEXT_PUBLIC_SERVER_URL=http://localhost:4000
# =============================================================================
# REQUIRED - DATABASE
# =============================================================================
POSTGRES_URL=postgresql://user:password@ep-xxx-yyy-123.us-east-2.aws.neon.tech/neondb?sslmode=require
# =============================================================================
# REQUIRED - STORAGE
# =============================================================================
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# =============================================================================
# REQUIRED - STRIPE PAYMENTS
# =============================================================================
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# =============================================================================
# OPTIONAL - CORS & SECURITY
# =============================================================================
REVEALUI_CORS_ORIGINS=http://localhost:3000,http://localhost:4000
Here is where to find each value:
| Variable | Where to get it |
|----------|----------------|
| REVEALUI_SECRET | Auto-generated by the CLI. To generate one manually: openssl rand -hex 32 |
| POSTGRES_URL | NeonDB Console — create a project, copy the connection string from the dashboard |
| BLOB_READ_WRITE_TOKEN | Vercel Dashboard — create a Blob store, copy the read/write token |
| STRIPE_SECRET_KEY | Stripe Dashboard — the key starting with sk_test_ |
| NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Same Stripe page — the key starting with pk_test_ |
| STRIPE_WEBHOOK_SECRET | Generated when you create a webhook endpoint (covered in Step 7) |
Warning: Never commit
.env.development.localto git. The CLI adds it to.gitignoreautomatically, but double-check before pushing.
Step 3: Initialize the database
With your POSTGRES_URL set, initialize the database schema:
pnpm db:init
Database Initializing database...
Database Database initialized successfully
Then run migrations to create all tables:
pnpm db:migrate
Database Running database migrations...
Database Migrations applied successfully
Finally, seed it with sample content:
pnpm db:seed
Seeding blog posts to http://localhost:4000...
Created: Getting Started with RevealUI
Created: Customizing Your Blog
Created: Draft Post Example
Seeding complete.
The seed script creates three sample blog posts — two published and one draft — so you have content to work with immediately.
Note: The seed script posts content via the REST API, so you will need the dev server running first (Step 4) if seeding separately. During initial setup, the CLI handles the order for you.
Step 4: Start development
Start the full development stack:
pnpm dev
Three services come up:
| Service | URL | What it does | |---------|-----|-------------| | CMS Admin | http://localhost:4000/admin | Admin dashboard — manage collections, users, and settings | | CMS API | http://localhost:4000/api | Auto-generated REST API for all your collections | | API Server | http://localhost:3004 | Standalone API with OpenAPI spec | | Swagger Docs | http://localhost:3004/docs | Interactive API documentation |
Open http://localhost:4000/admin in your browser. You should see the RevealUI admin login screen.
CMS ready http://localhost:4000
API ready http://localhost:3004
API docs http://localhost:3004/docs
Tip: Each app can be started independently. Use
pnpm dev:cmsfor just the CMS, orpnpm dev:apifor just the API server.
Step 5: Create your first collection
The scaffold already created a Posts collection for you. Open revealui.config.ts in the project root:
import config from '@revealui/config';
import { buildConfig, universalPostgresAdapter } from '@revealui/core';
import sharp from 'sharp';
import { Posts } from './src/collections/Posts';
export default buildConfig({
serverURL: config.reveal.publicServerURL || 'http://localhost:4000',
secret: config.reveal.secret,
db: config.database.url
? universalPostgresAdapter({ connectionString: config.database.url })
: universalPostgresAdapter({ provider: 'electric' }),
admin: {
user: 'users',
},
collections: [Posts],
globals: [],
plugins: [],
sharp,
});
Now look at the Posts collection in src/collections/Posts.ts:
import type { CollectionConfig } from '@revealui/contracts';
export const Posts: CollectionConfig = {
slug: 'posts',
labels: { singular: 'Post', plural: 'Posts' },
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
admin: {
description: 'URL-friendly identifier (e.g. "my-first-post")',
},
},
{
name: 'content',
type: 'richText',
},
{
name: 'status',
type: 'select',
defaultValue: 'draft',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
},
{
name: 'publishedAt',
type: 'date',
admin: {
description: 'When this post was published',
},
},
],
};
This single file gives you a fully typed collection with five fields. RevealUI automatically generates REST API endpoints for it:
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/posts | List all posts (with pagination and filtering) |
| GET | /api/posts/:id | Get a single post by ID |
| POST | /api/posts | Create a new post |
| PATCH | /api/posts/:id | Update a post |
| DELETE | /api/posts/:id | Delete a post |
Try it now — fetch your seeded posts:
curl http://localhost:4000/api/posts | jq
{
"docs": [
{
"id": "6651a2e3...",
"title": "Getting Started with RevealUI",
"slug": "getting-started-with-revealui",
"content": "Welcome to your new RevealUI blog!...",
"status": "published",
"publishedAt": "2026-03-17T12:00:00.000Z",
"createdAt": "2026-03-17T12:00:00.000Z",
"updatedAt": "2026-03-17T12:00:00.000Z"
},
...
],
"totalDocs": 3,
"limit": 10,
"page": 1,
"totalPages": 1
}
Let's add a new collection. Create src/collections/Pages.ts:
import type { CollectionConfig } from '@revealui/contracts';
export const Pages: CollectionConfig = {
slug: 'pages',
labels: { singular: 'Page', plural: 'Pages' },
access: {
read: () => true, // Anyone can read published pages
create: ({ req }) => !!req.user, // Only authenticated users can create
update: ({ req }) => !!req.user, // Only authenticated users can update
delete: ({ req }) => req.user?.role === 'admin', // Only admins can delete
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
},
{
name: 'content',
type: 'richText',
},
{
name: 'status',
type: 'select',
defaultValue: 'draft',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
},
],
};
Register it in revealui.config.ts:
import { Pages } from './src/collections/Pages';
import { Posts } from './src/collections/Posts';
// ...
collections: [Posts, Pages],
Save the file, and the dev server hot-reloads. You now have /api/pages with full CRUD and the access control rules you defined — no extra wiring needed.
Step 6: Add authentication
Open the admin dashboard at http://localhost:4000/admin and create your first admin account. Fill in an email and password (minimum 12 characters).
After signing up, RevealUI creates a session cookie (revealui-session) that authenticates all subsequent requests. This is session-based auth — no JWT tokens, no refresh token dance. The cookie is:
httpOnly— JavaScript cannot read itsecure— only sent over HTTPS (in production)sameSite=lax— prevents CSRF attacks
To verify access control is working, try accessing a protected endpoint without authentication:
# Unauthenticated — no cookie
curl -s http://localhost:4000/api/pages -X POST \
-H "Content-Type: application/json" \
-d '{"title": "Test", "slug": "test"}' | jq
{
"errors": [
{
"message": "You are not allowed to perform this action."
}
]
}
Now try with your session cookie (copy it from your browser's dev tools):
# Authenticated — with session cookie
curl -s http://localhost:4000/api/pages -X POST \
-H "Content-Type: application/json" \
-H "Cookie: revealui-session=<your-session-id>" \
-d '{"title": "About Us", "slug": "about", "status": "published"}' | jq
{
"doc": {
"id": "...",
"title": "About Us",
"slug": "about",
"status": "published",
"createdAt": "2026-03-17T12:05:00.000Z",
"updatedAt": "2026-03-17T12:05:00.000Z"
},
"message": "Page successfully created."
}
Access control is enforced at the database query level — there is no way to bypass it from the API.
Step 7: Connect Stripe
RevealUI handles Stripe integration out of the box — products, checkout sessions, subscriptions, and webhook processing.
7a. Set up the Stripe CLI for local webhook testing
# Install the Stripe CLI (macOS/Linux)
brew install stripe/stripe-cli/stripe
# Login to your Stripe account
stripe login
# Forward webhooks to your local server
stripe listen --forward-to localhost:4000/api/webhooks/stripe
The Stripe CLI prints your webhook signing secret:
> Ready! Your webhook signing secret is whsec_1234567890abcdef...
Copy that value into your .env.development.local:
STRIPE_WEBHOOK_SECRET=whsec_1234567890abcdef... # gitleaks:allow
Restart your dev server to pick up the new environment variable.
7b. Create a Stripe product with tier metadata
In the Stripe Dashboard, create a new product:
- Name: My Business Pro
- Price: $49/month (recurring)
- Metadata: Add a key
revealui_tierwith valuepro
The revealui_tier metadata key is what RevealUI uses to map Stripe products to license tiers. Valid tiers are free, pro, max, and enterprise.
7c. Test a checkout
RevealUI exposes a checkout API. Create a test checkout session:
curl -s http://localhost:4000/api/checkout \
-X POST \
-H "Content-Type: application/json" \
-H "Cookie: revealui-session=<your-session-id>" \
-d '{"priceId": "price_xxxxxxxxxxxxx"}' | jq
{
"url": "https://checkout.stripe.com/c/pay/cs_test_..."
}
Open that URL to complete the test checkout. Use Stripe's test card number 4242 4242 4242 4242 with any future expiry date and any CVC.
After checkout completes, the webhook fires and RevealUI automatically:
- Records the subscription
- Generates a license key tied to the customer
- Maps the
revealui_tiermetadata to the user's license tier - Activates feature gating for that tier
You can verify it worked in the admin dashboard under the Licenses section, or via the API:
curl -s http://localhost:4000/api/licenses \
-H "Cookie: revealui-session=<your-session-id>" | jq
Step 8: Deploy to Vercel
Your application is ready for production. Deploy it with three commands:
# Link to your Vercel account
vercel link
? Set up "~/my-business"? Yes
? Which scope? your-team
? Link to existing project? No
? What's your project's name? my-business
? In which directory is your code located? ./
Pull your environment variables to Vercel (or set them in the Vercel dashboard):
# Set production environment variables
vercel env add REVEALUI_SECRET production
vercel env add POSTGRES_URL production
vercel env add STRIPE_SECRET_KEY production
vercel env add STRIPE_WEBHOOK_SECRET production
vercel env add NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY production
vercel env add BLOB_READ_WRITE_TOKEN production
Deploy:
vercel deploy --prod
Vercel CLI 39.x.x
Deploying my-business...
Inspect: https://vercel.com/your-team/my-business/xxxxx
Production: https://my-business.vercel.app
Your application is live. Visit https://my-business.vercel.app/admin to access the admin dashboard in production.
Important: After deploying, update your Stripe webhook endpoint in the Stripe Dashboard to point to
https://my-business.vercel.app/api/webhooks/stripe. Also updateREVEALUI_PUBLIC_SERVER_URLandNEXT_PUBLIC_SERVER_URLto your production URL.
What you have built
Stop the clock. In roughly 10 minutes, you have a production-ready business application with:
- A CMS with typed collections and access control — define your data model in TypeScript, get a full admin UI and REST API automatically
- User authentication with session-based auth — secure by default with httpOnly cookies, brute force protection, and rate limiting
- A REST API with OpenAPI documentation — every collection gets CRUD endpoints with interactive Swagger docs at
/docs - Stripe billing with subscription management — checkout sessions, webhook handling, and automatic license generation
- License enforcement with feature gating — tier-based access control (
free,pro,max,enterprise) enforced at the API level - An admin dashboard — manage content, users, licenses, and settings from a single interface
- Deployed to production on Vercel — global edge network, automatic SSL, zero-config scaling
This is what "build your business, not your boilerplate" means. Every piece of infrastructure that software companies need — users, content, products, payments, and intelligence — is pre-wired and ready.
Next steps
You have the foundation. Here is where to go from here:
Add AI agents (Pro tier)
Upgrade to RevealUI Pro to unlock AI agents with CRDT memory, LLM orchestration, and task history:
export REVEALUI_LICENSE_KEY=<your-pro-license-key>
Connect MCP servers
RevealUI ships with open-source MCP (Model Context Protocol) servers for Stripe, Supabase, Neon, Vercel, and Playwright. Let your AI agents interact directly with your business infrastructure.
Add more collections
The pattern scales. Add products, orders, tickets, knowledge bases — each collection is a single TypeScript file with automatic API generation and access control.
Join the community
- Documentation — full reference for every package and API
- GitHub — star the repo, report issues, contribute
- Community Forum — ask questions, share what you are building
RevealUI is an agentic business runtime. Users, content, products, payments, and AI — pre-wired, open source, and ready to deploy. Get started at revealui.com.