Task Manager - React Query, Serverless API, & Express Reference Backend (FullStack React Fundamental Project 15)
This repository bundles two complementary projects:
task-manager/– The production-ready React + serverless application that ships a full task manager with colocated API routes for Vercel/Netlify deployments.task-manager-backend-reference/– A complete Express.js backend kept for educational purposes, local experimentation, and future scaling scenarios.
Use this repo to explore modern full-stack patterns, study code organization, and learn how to migrate from a traditional Node server to serverless functions without losing the original implementation.
- Vite-powered React app with real-time task management.
- Serverless REST API exposed under
/api/tasks, sharing code between Vercel and Netlify deployments. - React Query handles fetching, caching, optimistic updates, and background refetches.
- Local storage synchronization for near-instant UI hydration across page refreshes.
- Toast notifications for success/error feedback.
- Lightweight CSS styling and reusable components/hooks for easy reuse.
- Frontend: React 18, Vite, React Query (@tanstack/react-query), React Toastify.
- API: Node.js serverless functions (Vercel-esque handlers + Netlify wrappers).
- Utilities: Axios, nanoid, localStorage.
- Tooling: ES Modules, npm, Netlify/Vercel CLIs (optional).
task-manager/
├── api/
│ ├── _lib/
│ │ └── taskStore.js # Shared storage helpers
│ ├── tasks/
│ │ ├── index.js # GET/POST handler
│ │ └── [id].js # PATCH/DELETE handler
│ └── tasks.data.json # Seed dataset
├── netlify/
│ └── functions/ # Netlify compatible serverless functions
├── public/ # Static assets
├── src/
│ ├── App.jsx
│ ├── Form.jsx
│ ├── Items.jsx
│ ├── SingleItem.jsx
│ ├── reactQueryCustomHooks.jsx
│ ├── localStorageUtils.js
│ ├── utils.js
│ ├── index.css
│ └── main.jsx
├── netlify.toml
├── package.json
└── README.md
-
Install dependencies
cd task-manager npm install -
Start the development server
npm run dev
Visit
http://localhost:5173. The app defaults to the hosted backend unless configured otherwise (see environment variables). -
Optional: emulate serverless functions locally
- Vercel:
npx vercel dev - Netlify:
npx netlify dev
These commands allow the frontend to call the local
/api/tasksendpoints exactly as they’ll exist after deployment. - Vercel:
The frontend works without any .env file. Override settings only when you need to point to a different API base URL.
| Variable | Default | Purpose |
|---|---|---|
VITE_API_BASE_URL |
https://task-management-server-nyfr.onrender.com/api/tasks |
Overrides the Axios base URL defined in src/utils.js. Set to /api/tasks when deploying the bundled serverless API. |
cd task-manager
echo "VITE_API_BASE_URL=/api/tasks" > .env.localRestart npm run dev so Vite can pick up the new value. In Netlify/Vercel dashboards, add the same variable under project settings.
npm run dev– start Vite in development mode.npm run build– create a production build indist/.npm run preview– preview the production build locally.
Serverless functions deploy automatically; no additional script is required.
The frontend expects the following REST routes (served by serverless handlers or the reference backend):
{
"taskList": [{ "id": "abc", "title": "walk the dog", "isDone": false }]
}POST /api/tasks
Content-Type: application/json
{ "title": "Ship serverless" }{
"task": { "id": "xyz", "title": "Ship serverless", "isDone": false }
}PATCH /api/tasks/xyz
Content-Type: application/json
{ "isDone": true }DELETE /api/tasks/xyzEach mutation returns a confirmation message ({ "msg": "task updated" }, { "msg": "task removed" }).
main.jsxinitializes React, injects global styles, and wraps the tree inQueryClientProvider.App.jsxhouses the layout: toast notifications, task form, and task list.Form.jsxcaptures input, callsuseCreateTask, and clears the field on success.Items.jsxusesuseFetchTasksto display loading/error states and the currenttaskList.SingleItem.jsxhandles toggling completion via checkbox and deleting via button.reactQueryCustomHooks.jsxcentralizes fetching and mutation logic, includinglocalStoragesyncing.localStorageUtils.jsabstracts read/write/remove operations to keep components clean.utils.jsconfigures Axios to use the appropriate base URL.
Each component is self-contained, making it straightforward to port pieces into other applications.
const { data, isLoading, isError } = useQuery({
queryKey: ["tasks"],
queryFn: async () => {
const { data } = await customFetch.get("/");
return data;
},
initialData: () => {
const cached = readTasksFromStorage();
return cached ? { taskList: cached } : undefined;
},
});useFetchTasksseeds the cache fromlocalStorage, fetches from the network, and writes back on success.- Mutations update the cache immediately via
queryClient.setQueryData, write tolocalStorage, then triggerinvalidateQueriesfor server confirmation. - Toast notifications surface success or error states for create/edit/delete actions.
- Tasks are cached under the key
react-query-task-manager. - On load, the UI renders cached tasks instantly, then reconciles with server responses.
- This approach gives a pleasant pseudo-offline experience without extra backend complexity.
Form.jsx– swapuseCreateTaskfor any mutation hook to reuse the component in other contexts (e.g., shopping list, note taker).SingleItem.jsx– expects props{ id, title, isDone }; replace mutation hooks to integrate with different backends.- Hooks – point
customFetchto another API that matches the same response shape. Because logic lives in hooks, the UI remains agnostic to the persistence layer.
- Import the repository in Vercel.
- Set
VITE_API_BASE_URL=/api/tasksin Project Settings → Environment Variables. - Deploy – Vercel detects the Vite app and the
api/folder automatically.
-
Import the repository in Netlify.
-
Build command:
npm run build; Publish directory:dist. -
Environment variable:
VITE_API_BASE_URL=/api/tasks. -
Optional rewrites (if needed):
[[redirects]] from = "/api/tasks" to = "/.netlify/functions/tasks" status = 200 force = true [[redirects]] from = "/api/tasks/*" to = "/.netlify/functions/task/:splat" status = 200 force = true
Netlify automatically builds the functions defined in netlify/functions/.
Task manager, React Query, Vite, serverless API, Netlify Functions, Vercel functions, nanoid, Axios, localStorage, SPA, CRUD tutorial, full-stack React fundamental project.
The task-manager/ project demonstrates how to build a cohesive full-stack app by colocating the frontend and backend code. It provides production-ready defaults while remaining approachable for learners and easily adaptable for other domains.
- Captures the original Express implementation before migrating to serverless functions.
- Helpful for understanding middleware pipelines, routing, and persistence strategies in Node.
- Serves as a starting point for scaling into a dedicated backend service once the project grows (e.g., adding authentication or database integrations).
The reference backend is not used in the serverless deployment, but it mirrors the same API contract.
task-manager-backend-reference/
├── server.js # In-memory task list (nodemon start)
├── localDataServer.js # File-based persistence via tasks.json
├── tasks.json # Seed data
├── package.json # Express, cors, morgan, nanoid, nodemon
└── README.md # Detailed educational guide
- Express.js handles routes and middleware.
- cors allows the React frontend to make cross-origin requests during local development.
- morgan logs HTTP requests in development mode.
- nanoid generates unique task IDs.
- nodemon restarts the server automatically when files change.
cd task-manager-backend-reference
npm install
# In-memory server
npm start
# File-backed server (reads/writes tasks.json)
npm run local-serverOptional .env:
PORT=5000server.jsandlocalDataServer.jsdefault to5000ifPORTis not set.- Adjust
VITE_API_BASE_URLin the frontend tohttp://localhost:5000/api/taskswhen using this backend.
Handlers in both server.js and localDataServer.js match the serverless routes:
GET /api/tasks– returns{ taskList }.POST /api/tasks– validatestitle, creates a new task withisDone: false.PATCH /api/tasks/:id– togglesisDonebased on request body.DELETE /api/tasks/:id– removes a task.
The 404 middleware responds with Route does not exist for unmatched routes.
| File | Description | When to Use |
|---|---|---|
server.js |
In-memory array seeded on boot | Quick demos, ephemeral testing |
localDataServer.js |
Reads/writes tasks.json using fs/promises |
Local dev requiring simple persistence |
tasks.json ships with sample tasks mirroring the frontend’s defaults.
- Integrate the Express routes into larger Node/Express projects.
- Swap the
taskListarray for a real database (MongoDB, PostgreSQL, Prisma, etc.). - Extend the API with authentication, pagination, or filtering.
- Use the reference code as teaching material for REST fundamentals or Express middleware.
Because the API matches the serverless version, you can switch implementations without touching the React code—just change the base URL.
Feel free to use this project repository and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.
Enjoy building and learning! 🚀
Thank you! 😊
