A production-ready boilerplate for building full-stack applications with Next.js 15 on Cloudflare Workers, featuring D1 (SQLite) database, R2 object storage, and Clerk authentication.
- Next.js 15 - Latest React framework with App Router and Server Components
- Cloudflare Workers - Deploy globally on the edge with Workers
- D1 Database - Serverless SQLite database at the edge
- R2 Storage - Object storage compatible with S3 API
- Clerk Auth - Drop-in authentication and user management
- Drizzle ORM - Type-safe database queries and migrations
- TypeScript - Full type safety across the stack
- Tailwind CSS - Utility-first CSS framework
- Todo App Example - Full CRUD implementation as reference
- Node.js 18+ and npm
- Cloudflare account (free tier works)
- Clerk account (free tier works)
git clone https://github.com/lilpacy/cloudflare-next-boilerplate.git
cd cloudflare-next-boilerplate
npm install- Create a free account at clerk.com
- Create a new application
- Copy your publishable and secret keys
- Create a
.env.localfile:
cp .env.example .env.local- Update
.env.localwith your Clerk keys
# Production database
npm run db:create
# Preview/staging database
npm run db:create:previewCopy the database IDs from the output and update wrangler.jsonc:
- Replace
YOUR_D1_DATABASE_ID_HEREwith production database ID - Replace
YOUR_PREVIEW_D1_DATABASE_ID_HEREwith preview database ID
# Create all required buckets
npm run storage:create # Main storage
npm run storage:create:cache # Next.js cache
npm run storage:create:preview # Preview storageEdit wrangler.jsonc and replace all placeholder values:
database_id: Your D1 database IDsNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: Your Clerk publishable keyNEXT_PUBLIC_BASE_URL: Your production domain (or localhost for dev)NEXT_PUBLIC_GOOGLE_ANALYTICS_ID: (Optional) Your GA4 measurement ID
# For local development
npm run db:migrate:local
# For production (after first deploy)
npm run db:migrate:remotenpm run devVisit http://localhost:3000 to see your app!
├── app/ # Next.js App Router
│ ├── actions/ # Server Actions
│ ├── admin/ # Admin dashboard
│ ├── todos/ # Todo app example
│ ├── components/ # React components
│ └── styles/ # Global styles
├── lib/ # Shared utilities
│ ├── db/ # Database schema and client
│ ├── r2/ # R2 storage client
│ └── schemas/ # Zod validation schemas
├── drizzle/ # Database migrations
├── public/ # Static assets
├── wrangler.jsonc # Cloudflare Workers config
└── package.json # Dependencies and scripts
npm run dev- Start development server with Turbopacknpm run build- Build for productionnpm run start- Start production server locallynpm run lint- Run ESLintnpm run type-check- Run TypeScript type checking
npm run db:create- Create production databasenpm run db:create:preview- Create preview databasenpm run db:migrate:local- Run migrations locallynpm run db:migrate:remote- Run migrations on productionnpm run db:shell:local- Open local database shellnpm run db:shell:remote- Open remote database shell
npm run storage:create- Create main R2 bucketnpm run storage:create:cache- Create cache bucketnpm run storage:create:preview- Create preview bucket
npm run deploy- Build and deploy to Cloudflarenpm run preview- Build and preview before deploying
- Ensure all Cloudflare resources are created (D1, R2)
- Update
wrangler.jsoncwith actual IDs and values - Run migrations locally first:
npm run db:migrate:local - Deploy:
npm run deploy - Run remote migrations:
npm run db:migrate:remote
npm run deploySee .env.example for all available environment variables. For production, configure these in wrangler.jsonc under the vars section.
- Framework: Next.js 15 (App Router)
- Runtime: Cloudflare Workers
- Database: Cloudflare D1 (SQLite)
- Storage: Cloudflare R2 (S3-compatible)
- ORM: Drizzle ORM
- Auth: Clerk
- Styling: Tailwind CSS 4
- TypeScript: Full type safety
- Deployment: OpenNext for Cloudflare
This boilerplate includes a fully functional Todo app demonstrating:
- User authentication with Clerk
- CRUD operations with D1
- Server Actions for mutations
- Client/Server component patterns
- Type-safe database queries
- Form handling and validation
Visit /todos after signing in to see it in action.
If you want to start fresh without the Todo example:
- Delete
app/todos/directory - Delete
app/actions/todos.ts - Delete
lib/schemas/todo.ts - Update
lib/db/schema.tsto remove the todos table - Create new migration:
npx drizzle-kit generate - Update navigation in
app/components/navigation.tsx
- Define database schema in
lib/db/schema.ts - Generate migration:
npx drizzle-kit generate - Create validation schema in
lib/schemas/ - Implement Server Actions in
app/actions/ - Build UI in
app/using the example patterns
- "Table not found": Run
npm run db:migrate:localornpm run db:migrate:remote - "Database binding not found": Check
wrangler.jsoncconfiguration
- "Clerk not configured": Verify
.env.localhas correct keys - "Unauthorized": Ensure you're signed in and have proper permissions
- Build fails: Run
npm run type-checkto find TypeScript errors - Workers error: Verify all IDs in
wrangler.jsoncare correct - Database migrations: Ensure you ran
npm run db:migrate:remoteafter deployment
- Next.js Documentation
- Cloudflare Workers
- Cloudflare D1
- Cloudflare R2
- Clerk Documentation
- Drizzle ORM
- OpenNext for Cloudflare
MIT
Contributions are welcome! Please feel free to submit a Pull Request.