How to Build a REST API with Node.js and Express: A Hands-On Task Manager Tutorial
If you are wondering how to build a REST API with Node.js, this guide will walk you through the entire process by building something real: a simple task manager API. No abstract theory, no filler. By the end, you will have a working API with routes, a database, and tested endpoints in Postman.
This tutorial is written for beginners with basic JavaScript knowledge. We will use the latest stable versions of Node.js and Express available in 2026. (via https://blog.postman.com)

What You Will Build
A REST API for a task manager that supports the following operations:
- Create a new task
- Retrieve all tasks
- Retrieve a single task by ID
- Update an existing task
- Delete a task
We will map these actions to standard HTTP methods:
| Action | HTTP Method | Endpoint |
|---|---|---|
| Create task | POST | /api/tasks |
| Get all tasks | GET | /api/tasks |
| Get one task | GET | /api/tasks/:id |
| Update task | PUT | /api/tasks/:id |
| Delete task | DELETE | /api/tasks/:id |
Prerequisites
- Node.js 22 LTS or later installed (check with
node -v) - A code editor such as VS Code
- Postman installed for testing endpoints
- A free MongoDB Atlas account (or a local MongoDB instance)
Step 1: Scaffold the Node.js Project
Create a new folder and initialize a Node.js project:
mkdir task-manager-api
cd task-manager-api
npm init -y
Open the generated package.json and add "type": "module" so we can use modern ES module syntax.
Step 2: Install Dependencies
We only need a few packages to get started:
npm install express mongoose dotenv
npm install --save-dev nodemon
Here is what each package does:
- express: the web framework for routing and middleware
- mongoose: an ODM to interact with MongoDB
- dotenv: loads environment variables from a
.envfile - nodemon: restarts the server automatically during development
Add these scripts to your package.json:
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
Step 3: Create the Server Entry Point
Create a file called server.js in the root of your project:
import express from 'express';
import mongoose from 'mongoose';
import dotenv from 'dotenv';
import taskRoutes from './routes/tasks.js';
dotenv.config();
const app = express();
app.use(express.json());
app.use('/api/tasks', taskRoutes);
app.get('/', (req, res) => {
res.json({ message: 'Task Manager API is running' });
});
const PORT = process.env.PORT || 3000;
mongoose.connect(process.env.MONGO_URI)
.then(() => {
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
})
.catch(err => console.error('Database connection failed:', err));

Step 4: Configure Environment Variables
Create a .env file at the project root:
PORT=3000
MONGO_URI=mongodb+srv://YOUR_USER:[email protected]/taskmanager
Never commit this file. Add .env to your .gitignore.
Step 5: Define the Task Model
Create a folder called models and inside it a file called Task.js:
import mongoose from 'mongoose';
const taskSchema = new mongoose.Schema({
title: { type: String, required: true, trim: true },
description: { type: String, default: '' },
completed: { type: Boolean, default: false }
}, { timestamps: true });
export default mongoose.model('Task', taskSchema);
Step 6: Build the Routes
Create a folder called routes with a file called tasks.js:
import express from 'express';
import Task from '../models/Task.js';
const router = express.Router();
// Create a task
router.post('/', async (req, res) => {
try {
const task = await Task.create(req.body);
res.status(201).json(task);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Get all tasks
router.get('/', async (req, res) => {
const tasks = await Task.find().sort({ createdAt: -1 });
res.json(tasks);
});
// Get one task
router.get('/:id', async (req, res) => {
try {
const task = await Task.findById(req.params.id);
if (!task) return res.status(404).json({ error: 'Task not found' });
res.json(task);
} catch (err) {
res.status(400).json({ error: 'Invalid ID' });
}
});
// Update a task
router.put('/:id', async (req, res) => {
try {
const task = await Task.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true
});
if (!task) return res.status(404).json({ error: 'Task not found' });
res.json(task);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Delete a task
router.delete('/:id', async (req, res) => {
try {
const task = await Task.findByIdAndDelete(req.params.id);
if (!task) return res.status(404).json({ error: 'Task not found' });
res.json({ message: 'Task deleted' });
} catch (err) {
res.status(400).json({ error: 'Invalid ID' });
}
});
export default router;
Step 7: Start the Server
Run the development server:
npm run dev
If everything is configured correctly, you should see Server running on port 3000 in your terminal. There’s a good explainer over at dev.to.
Step 8: Test Your Endpoints with Postman
Open Postman and try the following requests one by one.
Create a task (POST)
- URL:
http://localhost:3000/api/tasks - Body (raw JSON):
{
"title": "Write blog post",
"description": "Publish the Node.js tutorial on coding4.net"
}
Get all tasks (GET)
Send a GET request to http://localhost:3000/api/tasks and you should see an array containing the task you just created.
Update a task (PUT)
Copy the _id from the response and send:
PUT http://localhost:3000/api/tasks/THE_ID_HERE
{
"completed": true
}
Delete a task (DELETE)
Send a DELETE request to http://localhost:3000/api/tasks/THE_ID_HERE. You should receive a confirmation message.

Step 9: Add Basic Error Handling Middleware
Robust APIs handle unexpected errors gracefully. Add this at the bottom of server.js, just before mongoose.connect:
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal server error' });
});
Best Practices to Keep in Mind
- Use versioning in URLs, for example
/api/v1/tasks, so future changes do not break clients - Validate input with a library like Zod or Joi before hitting the database
- Return proper HTTP status codes (201 for created, 404 for not found, 400 for bad request)
- Never store secrets in the codebase, always use environment variables
- Add authentication with JWT once your API grows past a basic prototype
- Enable CORS if a frontend from another domain will consume the API
Project Structure Recap
task-manager-api/
├── models/
│ └── Task.js
├── routes/
│ └── tasks.js
├── .env
├── .gitignore
├── package.json
└── server.js
Next Steps
You now have a working REST API. Here are ideas to push it further:
- Add user authentication with JWT tokens
- Deploy it to Render, Railway, or Fly.io
- Write automated tests with Vitest or Jest
- Document the endpoints with Swagger or Scalar
- Add rate limiting with
express-rate-limit
FAQ
How do I build my own REST API?
Choose a runtime (Node.js), a framework (Express), and a database (MongoDB or PostgreSQL). Define your resources, map them to HTTP methods, implement the routes, connect to the database, and test with a tool like Postman. This tutorial follows exactly that pattern.
Is Node.js a RESTful API?
Node.js is a JavaScript runtime, not an API by itself. However, it is one of the most popular platforms for building RESTful APIs thanks to frameworks like Express, Fastify, and NestJS. An in-depth look at it is worth the time.
Which is better, FastAPI or Node.js?
Both are excellent. FastAPI (Python) shines for data-heavy and machine learning use cases with built-in validation. Node.js is unbeatable when your team already writes JavaScript, when you need real-time features, or when you want a single language across frontend and backend.
How can a beginner learn REST APIs?
Start by understanding the four core HTTP methods (GET, POST, PUT, DELETE) and status codes. Then build small projects like the task manager in this tutorial. Testing endpoints with Postman helps solidify how requests and responses actually work.
Do I need Express to build a REST API with Node.js?
No, but it dramatically simplifies routing and middleware. Alternatives include Fastify (faster), Hono (minimal), and NestJS (opinionated, TypeScript first). For beginners, Express remains the easiest starting point.
Wrapping Up
You just learned how to build a REST API with Node.js using Express and MongoDB, tested it with Postman, and covered the best practices used in real production APIs. The task manager example is intentionally simple so you can extend it into whatever project you have in mind, be it a to-do app, a CRM, or the backend of a mobile app.
At coding4.net, we build custom APIs and backend systems for companies of all sizes. If you need help scaling this kind of project, feel free to reach out.

