A modern, full-stack todo application built with Next.js, TypeScript, Tailwind CSS, and Cloudflare D1 database using Prisma ORM.
- ✅ CRUD Operations: Create, read, update, and delete todos
- 🎯 Priority System: Set priorities (LOW, MEDIUM, HIGH) with color coding
- ✏️ Edit in Place: Click on any todo to edit directly
- 🎨 Modern UI: Responsive design with Tailwind CSS
- ⚡ Fast Database: Powered by Cloudflare D1 and Prisma ORM
- 🔄 Real-time Updates: Optimistic UI updates with error handling
- 📱 Mobile Friendly: Responsive design for all devices
- Frontend: Next.js 15, React 19, TypeScript
- Styling: Tailwind CSS v4
- Database: Cloudflare D1 (SQLite-compatible)
- ORM: Prisma with D1 adapter
- Icons: Lucide React
- Deployment: Cloudflare Pages
- Deployment: Cloudflare Pages (recommended)
- Node.js 18.18 or later
- npm or yarn
- Cloudflare account (for production D1 database)
-
Clone and install dependencies:
npm install
-
Set up the database:
# Generate Prisma client npx prisma generate # Create and migrate the local database npx prisma db push # Seed with sample data npm run db:seed
-
Start the development server:
npm run dev
-
Open your browser and navigate to
http://localhost:3000
The todo application uses a simple schema with the following model:
model Todo {
id Int @id @default(autoincrement())
title String
content String?
completed Boolean @default(false)
priority Priority @default(MEDIUM)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Priority {
LOW
MEDIUM
HIGH
}npx wrangler d1 create your-database-nameThis will output database configuration like:
[[d1_databases]]
binding = "DB"
database_name = "your-database-name"
database_id = "your-database-id"Add the database configuration to your wrangler.toml:
name = "your-app-name"
compatibility_date = "2024-12-05"
[[d1_databases]]
binding = "DB"
database_name = "your-database-name"
database_id = "your-database-id"Generate SQL migration from your Prisma schema:
npx prisma migrate diff \
--from-empty \
--to-schema-datamodel ./prisma/schema.prisma \
--script \
--output migrations/0001_initial.sql# Create migration file
npx wrangler d1 migrations create your-database-name initial_schema
# Copy the generated SQL to the migration file
# Then apply to local D1
npx wrangler d1 migrations apply your-database-name --local
# Apply to remote D1
npx wrangler d1 migrations apply your-database-name --remoteFor production, update your environment variables:
# Get these from Cloudflare dashboard
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_DATABASE_ID=your-database-id
CLOUDFLARE_D1_TOKEN=your-api-tokenCreate a prisma.config.ts file for production D1 support:
import path from 'node:path'
import type { PrismaConfig } from 'prisma'
import { PrismaD1HTTP } from '@prisma/adapter-d1'
import 'dotenv/config'
type Env = {
CLOUDFLARE_D1_TOKEN: string
CLOUDFLARE_ACCOUNT_ID: string
CLOUDFLARE_DATABASE_ID: string
}
export default {
earlyAccess: true,
schema: path.join('prisma', 'schema.prisma'),
migrate: {
async adapter(env) {
return new PrismaD1HTTP({
CLOUDFLARE_D1_TOKEN: env.CLOUDFLARE_D1_TOKEN,
CLOUDFLARE_ACCOUNT_ID: env.CLOUDFLARE_ACCOUNT_ID,
CLOUDFLARE_DATABASE_ID: env.CLOUDFLARE_DATABASE_ID,
})
},
},
} satisfies PrismaConfig<Env>- Connect your repository to Cloudflare Pages
- Set the build command:
npm run build - Set the output directory:
.next - Add environment variables in the Cloudflare Pages dashboard
- Deploy!
GET /api/todos- Fetch all todosPOST /api/todos- Create a new todoPATCH /api/todos/[id]- Update a todoDELETE /api/todos/[id]- Delete a todo
src/
├── app/
│ ├── api/todos/ # API routes
│ ├── globals.css # Global styles
│ ├── layout.tsx # Root layout
│ └── page.tsx # Home page
├── components/
│ ├── TodoForm.tsx # Todo creation form
│ ├── TodoItem.tsx # Individual todo item
│ └── TodoList.tsx # Main todo list component
├── lib/
│ └── prisma.ts # Prisma client setup
└── types/
└── todo.ts # TypeScript types
npm run dev- Start development servernpm run build- Build for productionnpm run start- Start production servernpm run lint- Run ESLintnpm run db:seed- Seed database with sample data
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
MIT License - see LICENSE file for details.