A full-stack todo application with user authentication, categories, and todo management. Built with Express + Drizzle ORM on the backend and React + TypeScript on the frontend.
Backend
- Node.js / Express 5
- TypeScript
- PostgreSQL (Neon)
- Drizzle ORM
- Zod validation
- JWT authentication (access + refresh tokens)
- bcrypt password hashing
Frontend
- React 19
- TypeScript
- Vite
- Lucide React (icons)
todo-with-drizzle/
├── backend/
│ ├── src/
│ │ ├── config/ — Environment validation
│ │ ├── constants/ — HTTP status codes, enums
│ │ ├── controllers/ — Route handlers
│ │ ├── db/ — Database connection and schema
│ │ ├── middlewares/ — Auth, validation, error handling
│ │ ├── routes/ — Express route definitions
│ │ ├── schemas/ — Zod validation schemas
│ │ ├── services/ — Business logic layer
│ │ ├── types/ — TypeScript declarations
│ │ ├── utils/ — JWT, error utilities
│ │ ├── app.ts — Express app setup
│ │ └── index.ts — Server entry point
│ ├── drizzle/ — SQL migrations
│ ├── drizzle.config.ts
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── components/ — React components
│ │ ├── hooks/ — Custom hooks (useAuth)
│ │ ├── lib/api/ — API client and service functions
│ │ ├── types/ — TypeScript interfaces
│ │ ├── App.tsx — Main app component
│ │ └── main.tsx — Entry point
│ └── package.json
├── docs/
│ ├── openapi.json — API specification
│ └── user-stories.md — Feature documentation
└── README.md
- Node.js 20+
- PostgreSQL database (via Neon or local)
cd backend
cp .env.example .envFill in your environment variables in .env:
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
PORT |
Server port (default: 3000) |
CORS_ORIGIN |
Frontend URL (default: http://localhost:5173) |
JWT_ACCESS_SECRET |
Secret for access tokens |
JWT_REFRESH_SECRET |
Secret for refresh tokens |
JWT_ACCESS_EXPIRES_IN |
Access token expiry (e.g., 15m) |
JWT_REFRESH_EXPIRES_IN |
Refresh token expiry (e.g., 7d) |
Generate strong JWT secrets:
node -e "console.log(require('crypto').randomBytes(64).toString('base64'))"Install and run:
cd backend
npm install
npm run devRun database migrations:
cd backend
npx drizzle-kit migratecd frontend
npm installCreate frontend/.env:
VITE_API_URL=http://localhost:3000/api/v1
Run:
cd frontend
npm run devSee docs/openapi.json for the full API specification.
POST /api/v1/auth/signup— Create accountPOST /api/v1/auth/login— Log inPOST /api/v1/auth/refresh— Refresh tokensPOST /api/v1/auth/logout— Log out
GET /api/v1/users/me— Current userPATCH /api/v1/users/me— Update profilePATCH /api/v1/users/me/password— Change passwordDELETE /api/v1/users/me/delete— Delete account
GET /api/v1/todos— List todos (filterable by status, categoryId)POST /api/v1/todos— Create todoGET /api/v1/todos/:todoId— Get single todoPATCH /api/v1/todos/:todoId— Update todoDELETE /api/v1/todos/:todoId— Delete todo
GET /api/v1/categories— List categoriesPOST /api/v1/categories— Create categoryPATCH /api/v1/categories/:categoryId— Update categoryDELETE /api/v1/categories/:categoryId— Delete categoryPOST /api/v1/todos/:todoId/categories/:categoryId— Assign category to todoDELETE /api/v1/todos/:todoId/categories/:categoryId— Remove category from todo
- User registration and authentication (JWT access + refresh tokens)
- Todo CRUD with inline editing
- Category management with todo assignment
- Status tracking (pending, in progress, done)
- Filtering by status and category
- Token refresh for persistent sessions
- Profile management (update name, email, username, password)
- Account deletion
- Input validation via Zod
- Responsive design
ISC