Skip to content

Fullstack Todo App - Elmar Angao (3-5 Years)#268

Open
eangao wants to merge 6 commits intoZeff01:mainfrom
eangao:elmar-angao/fullstack-3-5
Open

Fullstack Todo App - Elmar Angao (3-5 Years)#268
eangao wants to merge 6 commits intoZeff01:mainfrom
eangao:elmar-angao/fullstack-3-5

Conversation

@eangao
Copy link

@eangao eangao commented Feb 27, 2026

Fullstack todo application with user authentication and CRUD operations built for the 3-5 years experience level
assessment.

Tech Stack

  • Next.js 16 (App Router, Turbopack)
  • TypeScript (strict mode)
  • Tailwind CSS + shadcn/ui
  • NextAuth.js v5 (Credentials provider)
  • Prisma ORM + SQLite
  • bcrypt for password hashing

Features Delivered

  • ✅ User registration and authentication
  • ✅ Protected routes with session management
  • ✅ Create, read, update, delete todos
  • ✅ Toggle completion status
  • ✅ User data isolation (users only see their own todos)
  • ✅ Responsive mobile-first design
  • ✅ Loading states and error handling
  • ✅ Input validation on all endpoints

Approach

Architecture: Chose a standard Next.js App Router pattern with API routes for the backend. Used server components by
default and client components only where necessary (forms, interactive elements).

Authentication: Implemented NextAuth.js v5 with credentials provider rather than OAuth to keep the setup simple and
self-contained. Passwords are hashed with bcrypt (10 salt rounds).

Database: Selected SQLite with Prisma ORM for zero-configuration setup. This makes it easy for reviewers to run the
app without external database dependencies.

UI Components: Used shadcn/ui instead of building from scratch. This saved time while maintaining a professional,
accessible design system.

State Management: Kept it simple with React useState and fetch API. Avoided Redux/Zustand since the app state is
straightforward (todo list + auth status).

Trade-offs

SQLite over PostgreSQL: Chose SQLite for simplicity and portability. In production, I would use PostgreSQL with proper
connection pooling.

JWT Sessions over Database Sessions: Used NextAuth's JWT strategy instead of database sessions. Faster but requires
token rotation strategy for production.

Client-side fetching over Server Actions: Used traditional fetch in client components instead of Next.js Server
Actions for clarity and broader compatibility.

No Optimistic UI: Chose to wait for server confirmation before updating the UI. This is safer but slightly slower UX.
Would add optimistic updates in production.

No pagination: Implemented a simple "load all todos" approach. Would add pagination/infinite scroll for production
with many todos.

Basic error handling: Used toast-style notifications instead of a full error boundary system to keep it simple.

Setup Instructions

cd fullstack
npm install
cp .env.example .env
npx prisma generate && npx prisma db push && npx prisma db seed
npm run dev

Demo credentials: demo@example.com / password123

Build Status

✅ TypeScript strict mode - passing
✅ Production build - passing
✅ ESLint - passing
✅ All features working

---

<img width="605" height="547" alt="Screenshot 2026-02-27 172028" src="https://github.com/user-attachments/assets/4589ac1c-f3dd-4811-9faa-0177967870d0" />
<img width="887" height="633" alt="Screenshot 2026-02-27 171427" src="https://github.com/user-attachments/assets/7c90cb0c-e750-421f-899f-1873ee1ba07b" />
<img width="999" height="717" alt="Screenshot 2026-02-27 171931" src="https://github.com/user-attachments/assets/9ee9f288-5b0c-47bf-99bb-653d60b27209" />
<img width="950" height="585" alt="Screenshot 2026-02-27 171955" src="https://github.com/user-attachments/assets/5dae258c-40cb-4da2-998f-cb9276983f69" />



Submission by: Elmar Angao
Level: 3-5 Years Experience
Date: February 27, 2026

Session 1 — project foundation:
- Scaffold Next.js 16 (App Router, Turbopack, TypeScript, Tailwind)
- Install next-auth@5 beta, bcryptjs, Prisma 6, lucide-react
- Create Prisma schema (User + Todo models) with SQLite
- Create NextAuth v5 config (credentials provider, JWT, callbacks)
- Create /api/register route (POST with bcrypt + validation)
- Create /api/auth/[...nextauth] route handler
- Create middleware.ts to protect /todos routes
- Create shared types, Prisma singleton, cn() utility
- Add .env.example and .gitignore with proper exclusions
Implement Session 2: All todo CRUD API routes working with full test coverage.

Features:
- GET /api/todos: fetch user's todos with ownership isolation
- POST /api/todos: create new todo with validation (title required, trimmed)
- PATCH /api/todos/[id]: update todo with ownership verification
- DELETE /api/todos/[id]: delete todo with authorization checks

Test Coverage:
- 13 integration tests covering all CRUD operations
- Ownership isolation verified (users can't see/modify others' todos)
- Input validation tested (empty title, missing fields)
- Auth enforcement (401 for unauthenticated requests)
- Database operations (findMany, create, update, delete)

Security:
- Authentication enforced on all routes (getServerSession)
- Ownership verified with dual-field queries (id + userId)
- Input validation with whitespace trimming
- Proper HTTP status codes (200, 201, 400, 401, 404, 500)
- Type-safe responses using ApiResponse<T>

Testing:
- Vitest setup with Node environment
- Test utilities (createTestUser, cleanupDatabase)
- All tests passing (13/13)
- Type check passing (npx tsc --noEmit)
- Build passing (npm run build)

Files Created:
- /app/api/todos/route.ts (GET, POST)
- /app/api/todos/[id]/route.ts (PATCH, DELETE)
- /__tests__/api/todos.integration.test.ts (13 tests)
- /__tests__/helpers/test-utils.ts (test utilities)
- /vitest.config.ts (test configuration)
- /package.json (test scripts, dev dependencies)

Exit Criteria: All met ✓
- Add 8 shadcn/ui components (button, input, card, checkbox, badge, label, separator, skeleton)
- Create Providers wrapper for NextAuth SessionProvider
- Build AuthForm component with email validation and error handling
- Implement signin/signup pages with NextAuth integration
- Add Navbar with session-aware user display and sign-out
- Create protected /todos page with middleware protection
- Add comprehensive test coverage (39 tests across 6 test files)
- Fix TypeScript type errors and add accessibility attributes
- Verify all tests passing and production build successful
Implemented all Session 4 deliverables with test-first development:

Components (with tests):
- TodoItem.tsx: displays individual todos with checkbox, title, date, badge, delete button (16 tests)
- TodoList.tsx: renders todo list with empty/loading states (13 tests)
- CreateTodo.tsx: form to add new todos with validation (16 tests)
- TodoPageClient.tsx: client component for todo CRUD operations

Pages:
- Updated app/todos/page.tsx to integrate TodoPageClient
- app/page.tsx redirect logic verified (signin if no session, todos if authenticated)

Database:
- Created prisma/seed.ts with demo user (demo@example.com / password123) and 4 sample todos (2 pending, 2 completed)

Testing:
- 84 tests passing (9 test suites)
- TodoItem: 16 tests (rendering, styling, interactions, edge cases)
- TodoList: 13 tests (rendering, empty state, loading state, edge cases)
- CreateTodo: 16 tests (rendering, input, validation, multiple submissions)
- Plus 39 existing tests from Sessions 1-3 (auth, API, Navbar, AuthForm, Providers)

Build & Polish:
- npm run build passes with zero errors
- npx tsc --noEmit passes
- README.md with setup instructions and tech stack
- .env.example for reviewers
- Responsive design (mobile-first with Tailwind CSS)

Adhered to CLAUDE.md specifications:
- shadcn/ui components for UI
- Proper TypeScript types
- Error handling with console.error for debugging
- Input validation and ownership checks
- Immutable state patterns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant