This project is a vinext app that showcases a server-action powered todo list with incremental loading, filtering, and Postgres persistence via Drizzle ORM.
- Add todo items with client-side validation handled by Zod schemas and server actions.
- Filter by search text or completion status with debounced requests to avoid redundant fetches.
- Paginated loading that fetches additional pages when the user clicks Load more while preserving optimistic UI feedback.
- Toggle and delete todos through server actions that revalidate the
/route and redirect to keep the UI in sync. - Styled with DaisyUI themes layered on top of Tailwind CSS v4 in the vinext App Router.
- vinext (Next.js-compatible App Router on Vite)
- React 19 with
useTransition/useActionState - Drizzle ORM + PostgreSQL for schema-driven persistence
- Zod for schema validation
- use-debounce to throttle filter input
- DaisyUI / Tailwind CSS v4 for UI primitives
- DaisyUI provided components (cards, buttons, inputs) with Tailwind base utilities
- Bun 1.x (preferred runtime for scripts/builds) or Node.js 18+
- PostgreSQL database for the
todostable DATABASE_URLenvironment variable pointing to the Postgres instance- Docker & Docker Compose (optional, for containerized setup)
-
Install dependencies (Bun)
bun install
-
Create a
.env.localfile in the project root and set the database URL:DATABASE_URL=postgres://user:password@localhost:5432/dbname
-
Initialize the database
bun run db:generate bun run db:migrate # Optional: bun run db:seed (see app/lib/db/seed.ts) -
Start the development server
bun run dev
Visit http://localhost:3000 to open the todos experience.
- Defined in
app/lib/db/schema.tswith atodostable (id serial primary key, title text, completed boolean, createdAt timestamp). - Server actions live in
app/lib/modules/todo/actions.ts, leveragingdrizzle-ormto query, insert, update, and delete todos. - Refresh and redirect hooks (
revalidatePath,redirect) keep the UI aligned with the server state.
Use
bun run <script>(ornpm run <script>if you prefer Node).
dev: Run vinext in development modebuild: Compile for production with vinextstart: Start the production server with vinextlint: Run ESLintdb:generate: Generate Drizzle migrationsdb:migrate: Apply migrationsdb:seed: Seed the database with sample data (manual script)
- No automated tests are included yet. Use
npm run lintto keep the codebase consistent.
Dockerfileuses Bun multi-stage builds and theentrypoint.shscript to run Drizzle migrations before starting the vinext standalone server indist/standalone/server.js.docker-compose.ymlspins up:web: vinext server (port 3000) withDATABASE_URL=postgres://postgres:postgres@db:5432/todosdb: Postgres 17 (exposes 5433 on the host, persisting data inpostgres_datavolume)cron: lightweight Alpine container running a cron job that hitshttp://web:3000/db/clearevery 10 min (customize/remove as needed)
docker compose up --buildThe entrypoint inside the
webcontainer runsbun run db:generateandbun run db:migrateautomatically before launching the server.
Stop services:
docker compose down -v- Run
bun run build(ornpm run build) to produce an optimized build. - Deploy the
dist/standaloneoutput or container image to your hosting provider. - Provide a production
DATABASE_URLand run migrations (handled automatically in the provided container entrypoint).
For Cloudflare Workers, use vinext deploy. For other targets, vinext can also be paired with Nitro for Node, Vercel, Netlify, and other platforms.
