A modern, production-ready full-stack TypeScript monorepo starter built with Bun, Turborepo, Prisma, and React.
# Install dependencies
bun install
# Start PostgreSQL (Docker)
bun run db:start
bun run db:dev
# Start all dev servers
bun run devVisit http://localhost:3024 to see your app!
This starter provides a complete, opinionated stack with everything you need to build modern web applications:
- β‘οΈ Bun 1.3.9 β Fast runtime, package manager, and HTTP server
- ποΈ PostgreSQL 18.1 + Prisma 7.3 β Type-safe database with migrations
- π oRPC β Contract-first APIs with auto-generated OpenAPI spec
- π Audit Trail β Automatic change tracking with actor attribution
- π Real-time β LISTEN/NOTIFY for cross-instance awareness
- β° Scheduled Jobs β pg_cron for automatic cleanup
- βοΈ React 19.2 + Vite 7.3 β Modern UI with React Compiler
- π¨ shadcn/ui β 50+ beautiful, accessible components
- π― React Router 7 β Type-safe routing with loaders
- π TanStack Query 5 β Powerful data fetching and caching
- π Fumadocs β Beautiful MDX documentation rendering
- π Dark Mode β Built-in theme system
- π± Responsive β Mobile-first design
- π¦ Turborepo β Cached builds and parallel execution
- π TypeScript 5.9.3 β Strict mode, full type safety
- β¨ Biome β Fast formatting and linting
- π§ͺ Bun Test β Built-in test runner
- π§ Path Aliases β Clean imports across packages
- π JWT Tokens β Access (15m) + refresh (30d) with rotation
- π§ OTP β Email-based passwordless authentication
- π OIDC β Google and GitHub social login
- π WebAuthn β Hardware-backed passkeys
- π€ Actor Tracking β Know who made every change
packages/
βββ tools/ # Shared utilities (tryCatch, Temporal, prototypes)
βββ tsconfig/ # Shared TypeScript configurations
βββ database/ # Prisma schema, migrations, audit infrastructure
βββ server/ # Bun HTTP server with oRPC API
βββ api/ # Type-safe API client for frontend
βββ ui/ # shadcn/ui component library
βββ web/ # Main application (micro-frontend)
βββ docs/ # Documentation site (Fumadocs + MDX)
βββ public/
βββ content/ # MDX documentation files go here
bun run dev # Start all packages in development mode
bun run build # Build all packages for production
bun run typecheck # Type-check all packages
bun run lint # Lint all packages with Biome
bun run format # Format all files with Biomebun run db:start # Start PostgreSQL via Docker Compose
bun run db:dev # Run migrations + apply triggers (development)
bun run db:deploy # Run migrations + apply triggers (production)| Layer | Technology |
|---|---|
| Runtime | Bun 1.3.9 |
| Monorepo | Turborepo with Bun workspaces |
| Language | TypeScript 5.9.3 (strict mode) |
| Backend | Bun HTTP server, oRPC, Pino logging |
| Database | PostgreSQL 18.1, Prisma 7.3 (@prisma/adapter-pg) |
| Frontend | React 19.2, Vite 7.3, React Router 7, TanStack Query 5 |
| UI | shadcn/ui (base-vega), Tailwind CSS 4.1, Lucide icons |
| Auth | JWT (jose), OTP, OIDC (Google/GitHub), WebAuthn passkeys |
| Linting | Biome 2.3 (formatting + linting) |
| Validation | Zod 4.1 |
| Date/Time | Temporal API via temporal-polyfill |
// Define API once
export const getUser = authedProcedure
.input(z.object({ id: z.string() }))
.output(UserSchema)
.handler(async ({ input, context }) => {
return usersData.findById(context.db, input.id);
});
// Use with full type safety
const user = await api.users.getUser({ id: '123' });
// ^? User (fully typed!)Every database mutation knows who made it:
await withActor(db, userId, async (tx) => {
return tx.post.create({ data });
});
// Automatically logged in audit.change_logimport { listener } from '@rainestack/database';
listener.on('post', (event) => {
console.log(`Post ${event.operation}:`, event.id);
// Invalidate caches, broadcast to WebSocket clients, etc.
});import { Button } from '@rainestack/ui/components/ui/button';
import { Card } from '@rainestack/ui/components/ui/card';
import { useTheme } from '@rainestack/ui/providers/theme';
<Button variant="default">Click me</Button>import { tryCatch } from '@rainestack/tools/try-catch';
const { data, error } = await tryCatch(fetchUser(id));
if (error) return handleError(error);
console.log(data.name); // fully typed, no try/catch blocksCreate a .env file in the root:
# Required
DATABASE_URL=postgresql://dev_user:dev_password@localhost:5432/dev_db
JWT_SECRET=your-super-secret-key-at-least-32-characters-long
# Optional - OIDC Providers
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# Optional - WebAuthn
RP_ID=localhost
RP_NAME=RaineStack
RP_ORIGIN=http://localhost:3000Generate a secure JWT_SECRET:
# Using openssl
openssl rand -base64 32
# Using Bun
bun -e "console.log(require('crypto').randomBytes(32).toString('base64'))"This is a starter template β make it your own:
The Post model is just a placeholder. Replace it with your domain models:
// packages/database/prisma/schema.prisma
model YourModel {
id String @id @default(cuid())
// Your fields here
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Then create a migration:
cd packages/database
bunx --bun prisma migrate dev --name add_your_modelEdit Tailwind CSS variables in packages/ui/src/styles/theme.css:
:root {
--primary: 240 5.9% 10%; /* Your brand color */
--primary-foreground: 0 0% 98%;
/* ... */
}- Data Layer:
packages/server/src/data/β Database operations - Routes:
packages/server/src/routes/β API endpoints - Components:
packages/web/src/components/β UI components - Pages:
packages/web/src/routes/β Application pages
Choose which auth methods you need by setting environment variables. Remove unused providers from packages/server/src/routes/auth.ts.
Replace the docs content in packages/docs/src/app.tsx with your own documentation.
# Build all packages
bun run build
# Run migrations
bun run db:deploy
# Start server
cd packages/server
bun run dist/index.jsSet STATIC_DIR environment variable to the directory containing built frontend assets.
When you run bun run dev, documentation is available at:
- Docs: http://localhost:3024/docs
- API Spec: http://localhost:3000/api/openapi.json
- Health Check: http://localhost:3000/healthz
Create MDX files in packages/docs/public/content/:
---
title: Your Page Title
description: Page description
---
# Your Page Title
Your content here with full MDX support!For detailed project rules and architecture, see AGENTS.md.
All dependencies MUST be in the catalog with exact versions:
Always use withActor() for mutations:
// β
Correct
await withActor(db, userId, async (tx) => {
return tx.post.create({ data });
});
// β Wrong
await db.post.create({ data }); // No audit trail!Always check @rainestack/ui before creating new components:
// β
Use existing components
import { Button } from '@rainestack/ui/components/ui/button';
// Import from subpaths for tree-shaking
import { Card } from '@rainestack/ui/components/ui/card';Prefer tryCatch over try/catch:
// β
Type-safe error handling
const { data, error } = await tryCatch(operation());
if (error) return handleError(error);
// β Traditional try/catch
try {
const data = await operation();
} catch (error) {
handleError(error);
}MIT License - see LICENSE for details.
- Bun β Fast all-in-one JavaScript runtime
- Turborepo β High-performance monorepo build system
- Prisma β Next-generation ORM
- oRPC β Contract-first RPC framework
- shadcn/ui β Beautifully designed components
- Temporal API Polyfill β Modern date/time
βοΈ Star this repo if you find it useful!
GitHub β’ Issues β’ Documentation