CASE STUDY 02 / AUTHENTICATION & API DESIGN
Task Management System
A full-stack personal task manager with JWT authentication, coordinated token refresh, and user-scoped MongoDB persistence.
Overview
A personal task manager for creating, listing, completing, and deleting tasks with optional due dates. Its most substantial engineering work is the authentication lifecycle: coordinating concurrent requests during access-token refresh, maintaining revocable refresh tokens, and enforcing user ownership at the API boundary.
Problem
A small task application still needs to keep one user’s records separate from another’s and handle expired access tokens predictably. Concurrent API failures should not trigger duplicate refresh attempts within the same browser tab.
Requirements
- Create, list, complete or reopen, and delete the authenticated user’s tasks, with optional due dates.
- JWT access and refresh authentication with server-side refresh-token revocation.
- Validated API inputs, consistent errors, and user-scoped persistence.
- Incremental task loading with offset pagination, due-date ordering, and client-side filtering.
Architecture
- React client
- Express routes
- Zod validation
- Authentication
- Controllers
- MongoDB
Authentication and refresh coordination
Access tokens are sent through Authorization: Bearer. The refresh token is held in an HTTP-only cookie and stored against the user in MongoDB so the server can revoke it.
The Axios interceptor uses isRefreshing, failedQueue, and a _retry flag to coordinate concurrent failed requests behind one refresh operation within a browser tab. This avoids duplicate refresh attempts during access-token expiry.
Refresh produces a new access token only. Refresh-token rotation, reuse detection, token families, and hashed refresh-token storage are not implemented.
Validation and error handling
Express routes apply Zod validation and authentication before controllers. Request bodies, query strings, and route parameters can be validated independently, and controllers consume validated data rather than raw Express inputs.
asyncHandler forwards asynchronous controller failures. AppError and centralized error mapping return a consistent API response shape.
Task ownership and pagination
MongoDB is the system of record, with User and Todo models accessed through Mongoose. Ownership checks use userId so authenticated users operate on their own tasks.
Task queries use skip/limit offset pagination and due-date ordering. Fetching limit + 1 records determines whether another page exists without a separate existence query.
This is not cursor or keyset pagination. Deep offsets and changing due dates remain scalability and correctness concerns; compound query indexes are an improvement area.
Frontend and deployment
React 19, React Router 7, and TypeScript provide the SPA. An authentication context tracks auth state, Axios handles API requests, and task completion and deletion use optimistic updates. Filtering happens in the client.
The audited deployment places the frontend on Vercel and the Express 5 backend at a separate API domain. MongoDB persistence uses Mongoose 9. No traffic, latency, throughput, or measured speedup is claimed.
Dormant reminder scaffolding
The repository contains Redis configuration, Bull queue code, a reminder worker, and an email service abstraction. They are not functioning reminder-delivery features: scheduling is hard-disabled, the worker cannot run from the compiled build, and email sending is a console-log stub.
There is no working application caching layer. Redis is dormant for normal requests, so Redis caching, production background jobs, and cache-driven API improvements are not presented as implemented.
Testing status
No automated tests or CI pipeline existed at the time of the supplied audit. Adding backend tests for authentication, refresh coordination, ownership, validation, and pagination is a next step, not completed coverage.
What I would improve
- Implement refresh-token rotation and reuse detection, hash stored refresh tokens, strengthen refresh-cookie security, and add authentication rate limiting.
- Add compound indexes for user-scoped queries and replace offset pagination with keyset pagination.
- Move filtering, search, and sorting server-side; improve concurrency handling for optimistic updates.
- Add automated backend tests and a CI/CD pipeline.
- Add health/readiness endpoints and graceful shutdown.
- Either complete reminder processing or remove its dormant Redis/Bull scaffolding.
What I learned
The strongest work in this application is coordinating authentication recovery and making API boundaries explicit. Repository dependencies are not evidence of working features: cache, queue, and performance claims need an executable implementation and, where relevant, measurements.