A Todo List REST API built with TypeScript, Express, and JWT authentication. Perfect for learning modern frontend development with React Query, SWR, or any state management library.
Features: ✨ CRUD Operations • 🔐 JWT Authentication • 📄 Pagination & Infinite Scroll • 📚 Interactive Swagger Docs • ✅ Unit Tested
# 1. Clone & install
git clone <your-repository-url>
cd todos-server
npm install
# 2. Setup environment
cp .env.example .env.local
# 3. Run the server
npm run devServer runs at: http://localhost:8080
Swagger Docs: http://localhost:8080/api-docs
Swagger JSON: http://localhost:8080/api-docs.json
Deploy your own instance of this API in seconds:
After clicking the button above:
- Connect your GitHub account (if not already connected)
- Set Environment Variables in Vercel:
JWT_SECRET: A strong random string (e.g.,your-super-secret-key-change-this)API_URL: Fill with placeholder for now (e.g.,https://john-todo-api-server.vercel.app)- 💡 Tip: Use your expected project name +
.vercel.app - We'll update this with the actual URL after deployment
- 💡 Tip: Use your expected project name +
- Click "Deploy" and wait ~2 minutes
- After deployment completes:
- Copy your actual production URL from Vercel (e.g.,
https://john-todo-api-server.vercel.app) - Go to Settings → Environment Variables
- Edit
API_URLand replace with your actual production URL - Click "Save"
- Redeploy the project (Deployments → ... → Redeploy)
- Copy your actual production URL from Vercel (e.g.,
🎉 Done! Your API is live and Swagger docs will show the correct URL!
If you prefer manual setup:
# 1. Install Vercel CLI
npm i -g vercel
# 2. Deploy
vercel
# 3. Set environment variables
vercel env add JWT_SECRET
vercel env add API_URL
# 4. Deploy to production
vercel --prodA production-ready Todo List API designed specifically for learning modern frontend development. Most learning APIs are either too simple (just mock data) or too complex (full production systems). This one hits the sweet spot: real backend functionality with beginner-friendly documentation.
Perfect for building your portfolio or practicing with React Query, SWR, Zustand, or any modern state management library.
- ✅ Interactive API docs (Swagger UI)
- ✅ Real JWT authentication and authorization
- ✅ Both traditional and infinite scroll pagination
- ✅ Comprehensive error handling examples
- ✅ Unit tested with 90%+ coverage
This API uses JWT (JSON Web Token) Bearer authentication to protect /todos endpoints.
JWT is a secure way to authenticate API requests. After logging in, you receive a token that proves your identity for subsequent requests.
- Login → Get a JWT token
- Use the token → Include it in the
Authorization: Bearer <token>header for protected endpoints
In your .env.local file:
PORT=8080
JWT_SECRET=your-super-secret-key-change-this-in-production
JWT_EXPIRES_IN=24h
# API URL for Swagger documentation (optional)
# If not set, automatically detects:
# - Vercel deployment → uses VERCEL_URL
# - Local development → uses http://localhost:8080
# Only set this if you need to override auto-detection
API_URL=https://your-api.example.com💡 Smart URL Detection:
- Local Development: No configuration needed! Automatically uses
http://localhost:8080 - Vercel Deployment: Automatically detects deployment URL from
VERCEL_URL⚠️ For Production: SetAPI_URLin Vercel Environment Variables to use your production domain- Without
API_URL, it will use the auto-generated Vercel URL (e.g.,project-abc123.vercel.app)
- Custom Domain: Set
API_URLenvironment variable to your custom domain
📝 How to set API_URL in Vercel:
- Go to your project in Vercel Dashboard
- Navigate to Settings → Environment Variables
- Add new variable:
- Name:
API_URL - Value:
https://api-todo-server.vercel.app(your production URL) - Environment: Production (or all environments)
- Name:
- Redeploy your project
This ensures your Swagger documentation always points to the correct URL! ✨
Swagger UI is available at http://localhost:8080/api-docs
Authentication:
- All
/todosendpoints are protected with JWT - You must include the JWT token in the
Authorizationheader:Authorization: Bearer <your-token>
Step 1: Login to get a token
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"password123"}'Response:
{
"success": true,
"message": "Login successful",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "[email protected]",
"name": "user"
}
}
}Step 2: Use the token in protected endpoints
curl -X GET http://localhost:8080/todos \
-H "Authorization: Bearer <your-token>"The easiest way to generate frontend code (React Query, Types, Axios) that is compatible with this backend:
- Go to http://localhost:8080/api-docs.json
- Copy the entire JSON content
- Paste it to your AI Chat (ChatGPT/Claude/Cursor) with this prompt:
"Here is the backend API Swagger JSON. Please generate the frontend TypeScript types and React Query hooks for these endpoints. Use
Authorization: Bearerheader."
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/login |
User login |
| Method | Endpoint | Description |
|---|---|---|
| GET | /todos |
Get todos (paginated) |
| GET | /todos/scroll |
Get todos (infinite scroll) |
| POST | /todos |
Create todo |
| PUT | /todos/:id |
Update todo |
| DELETE | /todos/:todoId |
Delete todo |
All endpoints return proper HTTP status codes and detailed error messages. Perfect for learning error handling.
This isn't just a CRUD API. It's designed to teach you real-world patterns that you'll use in production applications.
- Implementing login/logout flows
- Token-based authentication
- Protected route handling
- Session management strategies
Works great with:
- React Query / TanStack Query (recommended)
- SWR
- RTK Query
- Zustand with async actions
- Jotai atoms
- Traditional pagination (
/todos) for page-based UIs - Infinite scroll (
/todos/scroll) for mobile-style feeds - Cursor-based pagination
- "Load more" patterns
- Update UI instantly before server confirms
- Rollback changes on errors
- Handle pending states
- Manage race conditions
- Parse different HTTP error codes
- Display validation errors
- Implement toast notifications
- Add retry logic
- Handle offline scenarios
- Skeleton screens
- Spinner components
- Progressive loading
- React Suspense integration
- Client validation with Zod
- Server validation messages
- Real-time feedback
- Field-level errors
- Client vs server-side filtering
- Multi-column sorting
- Debounced search
- Complex filter combinations
- Runtime: Node.js
- Framework: Express.js
- Language: TypeScript
- Validation: Zod
- Docs: Swagger/OpenAPI
src/
├── app.ts # Express config
├── routes/
│ ├── auth/ # Login endpoint
│ └── todos/ # CRUD endpoints
├── middlewares/ # API key validation
├── types/ # TypeScript types
└── mockup/ # Mock data
| Script | Description |
|---|---|
npm run dev |
Start dev server |
npm run build |
Build for production |
npm test |
Run tests |
npm run test:watch |
Run tests in watch mode |
npm run test:coverage |
Generate coverage report |
npm run lint |
Check linting |
Found a bug? Have an idea? Contributions are welcome! This project is meant to help people learn, so:
- Keep code beginner-friendly
- Add comments for complex logic
- Update tests for new features
- Maintain the documentation
Having trouble? Here's how to get help:
- Check the Swagger docs at http://localhost:8080/api-docs
- Look at the test files in
__tests__/for usage examples - Check the Troubleshooting section below
- Open an issue on GitHub
Problem: Swagger shows wrong URL (preview URL instead of production)
URL shows: https://project-abc123.vercel.app
Expected: https://api-todo-server.vercel.app
Solution:
- Go to Vercel Dashboard → Your Project
- Settings → Environment Variables
- Add
API_URLwith your production URL - Deployments → Click ... → Redeploy
Problem: "Cannot find module" errors after deployment
Solution:
# Make sure all dependencies are in dependencies, not devDependencies
npm install --save express cors swagger-ui-express
# Commit and push
git add package.json package-lock.json
git commit -m "fix: move dependencies"
git pushProblem: Environment variables not working
Solution:
- Check variable names are EXACTLY correct (case-sensitive)
- Make sure you selected the right environment (Production/Preview/Development)
- Redeploy after adding variables
- Check Vercel logs: Deployments → Click deployment → View Function Logs
Problem: API returns 404 on Vercel
Solution:
Check your vercel.json configuration:
{
"version": 2,
"builds": [
{
"src": "api/index.ts",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/api/index.ts"
}
]
}Problem: Port already in use
Solution:
Linux/Mac:
# Find and kill process on port 8080
lsof -ti:8080 | xargs kill -9Windows (PowerShell):
# Find process using port 8080
netstat -ano | findstr :8080
# Kill process by PID (replace <PID> with actual number)
taskkill /PID <PID> /F
# Example:
# netstat -ano | findstr :8080
# TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 12345
# taskkill /PID 12345 /FOr use a different port (all platforms):
PORT=3000 npm run devProblem: TypeScript errors
Solution:
# Clean install
rm -rf node_modules package-lock.json
npm install
# Rebuild
npm run buildISC