Build a Task Manager REST API

Build a CRUD REST API with Node.js and Express using modular routing.

Introduction

30 Second Summary

Every time you open a to-do app and your tasks load, or you mark one complete and it saves instantly, there's a server behind the scenes making that happen. That server speaks a language called REST.

In this project, you will build a task manager REST API from scratch using Node.js and Express. You'll create a server that can create, read, update, and delete tasks through HTTP requests.

What You'll Build

A local REST API server with five endpoints that handle full CRUD operations for tasks, returning JSON responses with proper HTTP status codes.

By the end of this project, you'll have:

  • A Node.js project with Express configured and a server running on your local machine.
  • Five working API endpoints that handle creating, reading, updating, and deleting tasks.
  • A modular route structure using Express Router that you can extend with new features.
  • Secret Mission: Add a filter feature that lets you query tasks by their completion status.

Are there any prerequisites?

You should have basic familiarity with JavaScript (variables, functions, objects, arrays). No prior backend or server experience is needed.

Before We Start

Every app you use on your phone or computer talks to a server behind the scenes. When you open a to-do app and your tasks appear, that's a REST API delivering data to your screen.

In this step, you'll set up everything you need to start building your own REST API. You'll install Node.js (the engine that runs your server code), open Visual Studio Code (your code editor), and create your project folder.

In this step, get ready to:
  • Install Node.js and verify it's working.
  • Open Visual Studio Code with an integrated terminal.
  • Create your project folder and initialize it with npm.
Download and install Node.js

Node.js is a JavaScript runtime that lets you run JavaScript outside the browser. Your REST API server will run on Node.js.

  • Open your web browser and go to https://nodejs.org.
  • Download the LTS version (v24). The page will detect your operating system and show the right installer.
  • Run the downloaded installer and accept all default settings.

What is LTS?

LTS stands for Long Term Support. It means this version gets bug fixes and security updates for a long time, making it the most stable choice for building projects.

After installation completes, you need to verify Node.js is working. Open a terminal to run the check.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.
  • Check your Node.js version by running this command:
node --version

✔️ I see version 24 or higher

Node.js is ready to go. Confirm that npm (Node's package manager) is also available by running:

npm --version

You should see a version number printed. Both tools are installed and working.

ⓧ I see an older version

You have Node.js installed, but it's an older version. You need version 24 or higher for this project.

  • Go back to https://nodejs.org and download the LTS version.
  • Run the installer. It will replace your existing version with the new one.
  • Close your terminal, reopen it, and run node --version again.
  • You should now see v24 or higher.

ⓧ Command not found

Node.js isn't installed on your machine yet.

  • Go to https://nodejs.org and download the LTS version.
  • Run the installer and accept all default settings.
  • Close your terminal completely, reopen it, and run node --version again.
  • You should now see v24 or higher.

Still seeing command not found?

Make sure you fully closed and reopened your terminal after installation. The terminal needs to restart to pick up the new Node.js path.

help me troubleshoot Node.js not being recognized after installation

Open Visual Studio Code

Visual Studio Code (VS Code) is the code editor you'll use to write your API. It has a built-in terminal so you can write code and run commands in the same window.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code and press Enter to open it.

✔️ VS Code opened

You're all set. Continue to the next instruction below.

ⓧ I don't have VS Code

  • Download VS Code from https://code.visualstudio.com.
  • Run the installer and accept the default settings.
  • Once installed, open VS Code using the search bar method above.

Now open the integrated terminal inside VS Code. This is where you'll run all your commands for the rest of the project.

  • Press Ctrl+` (backtick key, top-left of keyboard) to open the integrated terminal.

Where is the backtick key?

The backtick (`) is usually the key to the left of the number 1, below the Escape key. Press Ctrl and that key together to toggle the terminal panel.

Create your project folder

Your project needs its own folder to keep all the files organized. You'll create this folder on your Desktop so it's easy to find.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir task-api
cd task-api
  • Initialize your Node.js project by running:
npm init -y

What does this command do?

The npm init -y command creates a package.json file in your project folder. This file tracks your project's name, version, and any packages (libraries) you install later.

The -y flag accepts all the default settings so you don't have to answer questions one by one.

  • Confirm the file was created by running:
ls

You should see package.json listed in the output.

Don't see package.json?

Make sure you ran cd task-api before running npm init -y. The package.json file is created in whatever folder your terminal is currently in.

help me figure out why npm init didn't create my package.json file

✔️ Awesome, I've got everything!

Great. Make sure VS Code is open and your terminal is in the task-api folder.

ⓧ I'd like to double check the full code

Here's what your package.json should look like:

{
  "name": "task-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Your development environment is ready and your project is initialized. Next up, you'll install Express and create your first server file.

Install Express and Create Your Server

Your project folder is ready and Node.js is installed. Now it's time to build something that actually responds to requests.

Express is a web framework for Node.js that makes it simple to define routes and handle HTTP requests. In this step, you'll install Express, configure your project for modern JavaScript syntax, and create a server that sends back a JSON response.

In this step, get ready to:
  • Install Express as a project dependency.
  • Configure your project to use ES modules.
  • Create and test your Express server.
Install Express
  • Install Express as a dependency by running this command:
npm install express@5.2.1
  • You should see output confirming packages were added, including a line like added 1 package.
  • Confirm a node_modules folder appeared by running:
ls
  • You should see node_modules, package.json, and package-lock.json in the output.

npm install failing?

Check that you're inside the task-api folder by running pwd (macOS/Linux) or cd (Windows). The output should end with task-api.

If you see a network error, check your internet connection and try running the command again.

help me fix npm install errors

Configure your project for ES modules

ES modules are the modern way to share code between JavaScript files. They use import and export syntax instead of the older require() pattern.

  • Open package.json in VS Code by clicking on it in the Explorer sidebar.
  • If you don't see the file listed, click File, then Open Folder, and select the task-api folder.
  • Select all the contents of package.json and replace them with this code:
{
  "name": "task-api",
  "version": "1.0.0",
  "description": "A simple task manager REST API",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "5.2.1"
  }
}

What changed in this file?

  • "type": "module" tells Node.js to use modern import/export syntax instead of require().
  • "main": "server.js" points to the entry file you'll create next.
  • "start": "node server.js" adds a convenient script so you can run npm start later.
  • The dependencies section records that Express 5.2.1 is installed.
  • Save package.json by pressing Ctrl+S.

Seeing red underlines in package.json?

Make sure every string is wrapped in double quotes and that commas separate each key-value pair. JSON does not allow trailing commas after the last item in an object or array.

help me fix JSON syntax errors in my package.json

✔️ Awesome, I've got everything!

Great. Double check you've saved package.json before moving on.

ⓧ I'd like to double check the full code

{
  "name": "task-api",
  "version": "1.0.0",
  "description": "A simple task manager REST API",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "5.2.1"
  }
}
Create your server file

Your server file is the entry point for your application. It sets up Express, configures middleware, and starts listening for incoming requests.

  • In the Explorer sidebar, hover over the task-api folder name and click the New File icon.
  • Name the file server.js and press Enter.
  • Paste the following code into server.js:
import express from 'express';

const app = express();
const PORT = 3000;

// Middleware to parse JSON request bodies
app.use(express.json());

// Root route
app.get('/', (req, res) => {
  res.json({ message: 'Task Manager API is running' });
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

What does this code do?

  • import express from 'express' loads the Express framework using ES module syntax.
  • express() creates your application instance. Every route and setting attaches to this app object.
  • app.use(express.json()) is middleware that automatically parses JSON data from incoming requests, so you can read it in your route handlers.
  • app.get('/', ...) defines a route that responds to GET requests at the root URL with a JSON message.
  • app.listen(PORT, ...) starts the server on port 3000 and prints a confirmation to the terminal.
  • Save server.js by pressing Ctrl+S.
  • Start your server by running this command in the terminal:
node server.js
  • You should see Server is running on http://localhost:3000 printed in your terminal.
  • Open your browser and visit http://localhost:3000.
  • You should see a JSON response: {"message":"Task Manager API is running"}.

Stopping the server

Press Ctrl+C in the terminal to stop the server whenever you need to make changes. You'll restart it with node server.js after editing files.

Server not starting?

If you see Cannot find module 'express', make sure you ran npm install express@5.2.1 earlier and that your terminal is inside the task-api folder.

If you see EADDRINUSE :::3000, another process is using port 3000. Close any other terminal windows running a server, then try again.

If you see a syntax error, double check that server.js matches the code above exactly.

help me fix my Express server startup error

✔️ Awesome, I've got everything!

Great. Double check you've saved server.js and that the server is running in your terminal.

ⓧ I'd like to double check the full code

import express from 'express';

const app = express();
const PORT = 3000;

// Middleware to parse JSON request bodies
app.use(express.json());

// Root route
app.get('/', (req, res) => {
  res.json({ message: 'Task Manager API is running' });
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Your server is up and responding to requests. Next up, you'll create a dedicated routes file and build the endpoints that let you create, read, update, and delete tasks.

Build Your Task Routes

Your server is running and responding to requests. Now it is time to give it something meaningful to do.

Right now, every route lives inside server.js. As your API grows, stuffing all your logic into one file becomes unmanageable. Express solves this with its Router feature, which lets you group related routes into separate files.

In this step, get ready to:
  • Create a routes/tasks.js file with a Router instance and in-memory task storage.
  • Build GET and POST route handlers that return JSON responses.
  • Mount the tasks router in server.js so your API can handle requests at /tasks.
Create the routes directory and file

Keeping routes in their own folder means each resource (tasks, users, projects) gets its own file. This keeps your codebase organized as it grows.

  • Switch back to your terminal in VS Code.
  • Stop the running server by pressing Ctrl+C.
  • Create the routes directory by running this command:
mkdir routes
  • Confirm the folder was created by running:
ls

You should see routes listed alongside node_modules, package.json, and server.js.

  • Create a new file called tasks.js inside the routes folder. In the VS Code Explorer sidebar, right-click the routes folder and select New File.
  • Name the file tasks.js and press Enter.
  • Add the initial router setup by pasting this code into routes/tasks.js:
import { Router } from 'express';

const router = Router();

// In-memory task storage
let tasks = [];
let nextId = 1;

export default router;

What does this code do?

This sets up the skeleton of your route file.

  • import { Router } from 'express' pulls in the Router class so you can define routes outside of server.js.
  • tasks is an empty array that acts as your database for now. Every task you create gets stored here in memory.
  • nextId tracks the next available ID number so every task gets a unique identifier.
  • export default router makes this router available for other files to import.
  • Save routes/tasks.js.

Seeing a syntax error on the import line?

Make sure your package.json includes "type": "module". Without this setting, Node.js defaults to CommonJS and does not recognize import statements.

Help me fix an import error in my routes/tasks.js file

Implement GET and POST routes

A REST API uses different HTTP methods to perform different actions. GET retrieves data. POST creates new data.

  • In routes/tasks.js, find the line that says export default router; and paste the following code above it:
// GET /tasks - List all tasks
router.get('/', (req, res) => {
  res.json(tasks);
});

// POST /tasks - Create a new task
router.post('/', (req, res) => {
  const { title } = req.body;
  if (!title) {
    return res.status(400).json({ error: 'Title is required' });
  }
  const task = {
    id: nextId++,
    title,
    completed: false
  };
  tasks.push(task);
  res.status(201).json(task);
});

What does this code do?

These two route handlers respond to requests that hit your /tasks endpoint.

  • router.get('/') handles GET requests by returning the entire tasks array as JSON.
  • router.post('/') handles POST requests. It pulls the title from the request body, validates it exists, builds a new task object, and adds it to the array.
  • res.status(201) sets the response status to 201 (Created), which signals that a new resource was successfully created.
  • res.status(400) returns a 400 (Bad Request) error if the client forgets to include a title.
  • Save routes/tasks.js.

Not sure where to paste the code?

The route handlers go between the let nextId = 1; line and the export default router; line. Make sure there is a blank line separating each section.

Help me figure out where to add my route handlers in tasks.js

✔️ Awesome, I've got everything!

Great. Double check you have saved routes/tasks.js before moving on.

ⓧ I'd like to double check the full code

Here is the complete routes/tasks.js file at this point:

import { Router } from 'express';

const router = Router();

// In-memory task storage
let tasks = [];
let nextId = 1;

// GET /tasks - List all tasks
router.get('/', (req, res) => {
  res.json(tasks);
});

// POST /tasks - Create a new task
router.post('/', (req, res) => {
  const { title } = req.body;
  if (!title) {
    return res.status(400).json({ error: 'Title is required' });
  }
  const task = {
    id: nextId++,
    title,
    completed: false
  };
  tasks.push(task);
  res.status(201).json(task);
});

export default router;
Mount the router in server.js

Your route file is ready, but server.js does not know about it yet. You need to import the router and tell Express which URL path it handles.

  • Open server.js in VS Code.
  • Find this line at the top of the file:
import express from 'express';
  • Add this import line directly below it:
import tasksRouter from './routes/tasks.js';
  • Now find the line that says // Root route and add the following code above it:
// Mount the tasks router
app.use('/tasks', tasksRouter);

What does mounting a router mean?

app.use('/tasks', tasksRouter) tells Express to forward any request that starts with /tasks to your tasks router. Inside the router, router.get('/') becomes GET /tasks from the outside world.

This pattern lets you build your API one resource at a time. Each resource gets its own router file, and server.js stays clean.

  • Save server.js.
  • Restart the server by running this command in your terminal:
node server.js
  • Open your browser and visit http://localhost:3000/tasks.

You should see an empty JSON array: []. This confirms your tasks router is mounted and the GET handler is responding correctly.

Seeing "Cannot GET /tasks"?

This means the router is not mounted properly. Check that your server.js has the import tasksRouter from './routes/tasks.js'; line at the top and app.use('/tasks', tasksRouter); before the root route.

Also confirm you saved both files and restarted the server after making changes.

Help me debug why my /tasks route is not working

✔️ Awesome, I've got everything!

Great. Double check you have saved server.js before moving on.

ⓧ I'd like to double check the full code

Here is the complete server.js file at this point:

import express from 'express';
import tasksRouter from './routes/tasks.js';

const app = express();
const PORT = 3000;

// Middleware to parse JSON request bodies
app.use(express.json());

// Mount the tasks router
app.use('/tasks', tasksRouter);

// Root route
app.get('/', (req, res) => {
  res.json({ message: 'Task Manager API is running' });
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Your API now has a dedicated route file that can list and create tasks. Next up, you will add the ability to update and delete individual tasks by their ID.

Add Update and Delete Endpoints

Your API can list all tasks and create new ones. That covers the "C" and the first "R" in CRUD (Create, Read, Update, Delete). But a real task manager needs to let you check on a specific task, mark it as done, or remove it entirely.

In this step, you'll add three more route handlers to finish the full CRUD set. Each one uses a route parameter to target a specific task by its ID.

In this step, get ready to:
  • Add a GET route that retrieves a single task by its ID.
  • Add a PUT route that updates a task's title or completion status.
  • Add a DELETE route that removes a task from the array.
Implement GET by ID

Listing all tasks is useful, but clients often need to fetch one specific task. A route parameter like /:id captures the value from the URL and makes it available in your handler.

  • In routes/tasks.js, find the comment that says // POST /tasks - Create a new task.
  • Add the following GET by ID handler directly above that comment:
// GET /tasks/:id - Get a single task
router.get('/:id', (req, res) => {
  const task = tasks.find(t => t.id === parseInt(req.params.id));
  if (!task) {
    return res.status(404).json({ error: 'Task not found' });
  }
  res.json(task);
});

What does this code do?

  • req.params.id grabs whatever value appears after /tasks/ in the URL. It always arrives as a string, so parseInt() converts it to a number for comparison.
  • tasks.find() searches the array for a matching task. If nothing matches, it returns undefined.
  • The if (!task) check sends a 404 status with an error message when the task doesn't exist. This tells the client the resource wasn't found.
  • Save routes/tasks.js.
  • In your terminal, stop the running server with Ctrl+C, then restart it by running:
node server.js
  • Open your browser and visit http://localhost:3000/tasks/1. You should see {"error":"Task not found"} because no tasks exist yet. The 404 response confirms your route is working.

Seeing a 'Cannot GET' message instead?

  • Make sure you placed the new route inside the router (between the existing routes and the export statement), not outside it.
  • Check that you restarted the server after saving. Changes only take effect after a restart.

Help me debug why my GET /:id route isn't responding.

Implement PUT (update)

The PUT HTTP method signals that the client wants to update an existing resource. Your handler will accept new values for title and completed from the request body, but only update the fields that are actually sent.

  • In routes/tasks.js, scroll down past the closing }); of the POST handler.
  • Add the following PUT handler below it:
// PUT /tasks/:id - Update a task
router.put('/:id', (req, res) => {
  const task = tasks.find(t => t.id === parseInt(req.params.id));
  if (!task) {
    return res.status(404).json({ error: 'Task not found' });
  }
  const { title, completed } = req.body;
  if (title !== undefined) task.title = title;
  if (completed !== undefined) task.completed = completed;
  res.json(task);
});

What does this code do?

  • The handler first looks up the task by ID, returning 404 if it doesn't exist.
  • It destructures title and completed from req.body. The !== undefined checks ensure you only overwrite fields the client actually sent. This way a client can update just the title without accidentally resetting completed.
  • After updating, it returns the full updated task object as JSON.
  • Save routes/tasks.js.

You won't test this endpoint yet. The next chunk adds DELETE, and then you'll restart the server with the full set of routes.

Syntax error when you save?

  • Make sure the previous POST handler's closing brace and parenthesis end with a semicolon before your new PUT handler starts.
  • Check that you haven't accidentally placed the new code inside the POST handler's curly braces.

Help me fix my PUT route syntax.

Implement DELETE

The final piece of CRUD is deletion. A DELETE request removes a resource entirely and returns a 204 status code, which means "success, but no content to send back."

  • In routes/tasks.js, add the following DELETE handler directly below the PUT handler you just added:
// DELETE /tasks/:id - Delete a task
router.delete('/:id', (req, res) => {
  const index = tasks.findIndex(t => t.id === parseInt(req.params.id));
  if (index === -1) {
    return res.status(404).json({ error: 'Task not found' });
  }
  tasks.splice(index, 1);
  res.status(204).send();
});

What does this code do?

  • findIndex() returns the position of the matching task in the array, or -1 if nothing matches. You need the index (not the object itself) so you can remove it.
  • tasks.splice(index, 1) removes exactly one element at that position from the array.
  • Status 204 means the server successfully processed the request but has nothing to send back. The .send() call closes the response with an empty body.
  • Save routes/tasks.js.
  • In your terminal, stop the server with Ctrl+C and restart it by running:
node server.js
  • Confirm you see Server is running on http://localhost:3000 in the terminal. This means all five route handlers loaded without errors.

Server won't start?

  • Look at the error message in the terminal. A SyntaxError with a line number tells you exactly where to look for a missing bracket or semicolon.
  • Make sure each handler ends with }); and that export default router; is still the very last line of the file.

Help me debug why my server won't start after adding routes.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file and that the server is running without errors.

ⓧ I'd like to double check the full code

Here's the complete routes/tasks.js file with all five route handlers:

import { Router } from 'express';

const router = Router();

// In-memory task storage
let tasks = [];
let nextId = 1;

// GET /tasks - List all tasks
router.get('/', (req, res) => {
  res.json(tasks);
});

// GET /tasks/:id - Get a single task
router.get('/:id', (req, res) => {
  const task = tasks.find(t => t.id === parseInt(req.params.id));
  if (!task) {
    return res.status(404).json({ error: 'Task not found' });
  }
  res.json(task);
});

// POST /tasks - Create a new task
router.post('/', (req, res) => {
  const { title } = req.body;
  if (!title) {
    return res.status(400).json({ error: 'Title is required' });
  }
  const task = {
    id: nextId++,
    title,
    completed: false
  };
  tasks.push(task);
  res.status(201).json(task);
});

// PUT /tasks/:id - Update a task
router.put('/:id', (req, res) => {
  const task = tasks.find(t => t.id === parseInt(req.params.id));
  if (!task) {
    return res.status(404).json({ error: 'Task not found' });
  }
  const { title, completed } = req.body;
  if (title !== undefined) task.title = title;
  if (completed !== undefined) task.completed = completed;
  res.json(task);
});

// DELETE /tasks/:id - Delete a task
router.delete('/:id', (req, res) => {
  const index = tasks.findIndex(t => t.id === parseInt(req.params.id));
  if (index === -1) {
    return res.status(404).json({ error: 'Task not found' });
  }
  tasks.splice(index, 1);
  res.status(204).send();
});

export default router;

Your API now supports all four CRUD operations. Next up, you'll put every endpoint through its paces using curl commands from a second terminal window.

Test Your API with curl

Your API has all five endpoints wired up and the server is running. Now it's time to prove everything works by sending real HTTP requests and inspecting the responses.

You'll use curl to talk directly to your server from the command line. Each request hits a different endpoint and uses a different HTTP method, so you'll see your entire API in action.

In this step, get ready to:
  • Create a task with a POST request and confirm it comes back with an ID.
  • Read, update, and delete tasks using GET, PUT, and DELETE requests.
  • Test an error case and confirm your API returns a 404 response.
Create a task

Your server is already running in your current terminal, so you need a second terminal to send requests to it.

  • Open a new terminal in VS Code by pressing Ctrl+Shift+` (backtick).
  • Navigate into your project folder by running this command:
cd ~/Desktop/task-api

Why a second terminal?

Your first terminal is busy running the server process. If you type commands there, the server stops. The second terminal lets you send requests while the server keeps listening.

  • Create your first task by running this curl command:
curl -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d "{\"title\":\"Learn REST APIs\"}"

What does this command do?

  • -X POST tells curl to send a POST request (creating a new resource).
  • -H "Content-Type: application/json" sets the request header so the server knows you're sending JSON.
  • -d sends the JSON body containing the task title.
  • Confirm you see this JSON response in your terminal:
{"id":1,"title":"Learn REST APIs","completed":false}

The server assigned an id of 1 and set completed to false by default. Your POST handler is working.

Seeing 'connection refused'?

  • Make sure your server is still running in the first terminal. You should see Server is running on http://localhost:3000 there.
  • If you accidentally stopped it, switch to the first terminal and run node server.js again.

Help me debug a curl connection refused error when testing my Express server.

Read, update, and delete tasks

Now that you have a task in your API, let's exercise the remaining endpoints to confirm each HTTP method works correctly.

  • Retrieve all tasks by running:
curl http://localhost:3000/tasks
  • Confirm you see an array containing your task:
[{"id":1,"title":"Learn REST APIs","completed":false}]
  • Retrieve the single task by its ID by running:
curl http://localhost:3000/tasks/1
  • Confirm you see the individual task object (not wrapped in an array this time):
{"id":1,"title":"Learn REST APIs","completed":false}
  • Update the task's completion status by running this PUT request:
curl -X PUT http://localhost:3000/tasks/1 -H "Content-Type: application/json" -d "{\"completed\":true}"
  • Confirm the response shows completed is now true:
{"id":1,"title":"Learn REST APIs","completed":true}
  • Delete the task by running:
curl -X DELETE http://localhost:3000/tasks/1

You won't see any output in the terminal. A 204 status code means the server successfully deleted the resource but has no content to send back.

  • Verify the task is actually gone by running:
curl http://localhost:3000/tasks
  • Confirm you see an empty array:
[]

What just happened?

You walked through the full lifecycle of a resource: create it (POST), read it (GET), update it (PUT), and delete it (DELETE). This pattern is called CRUD and it's the backbone of almost every web application.

Getting unexpected results?

  • If GET returns an empty array before you expect it, your server may have restarted between commands. Restarting clears the in-memory storage. Run the POST command again to recreate a task.
  • If PUT or DELETE returns a 404, make sure the ID in your URL matches the ID from your POST response.

Help me figure out why my curl commands are returning unexpected results.

Test error cases

A solid API doesn't just handle happy paths. It also responds gracefully when something goes wrong.

  • Request a task ID that doesn't exist by running:
curl http://localhost:3000/tasks/999
  • Confirm you see a 404 error response with a clear message:
{"error":"Task not found"}

Why test for errors?

In production, clients will send bad requests all the time. Testing error cases confirms your API returns helpful messages instead of crashing. The 404 status code tells the client the resource doesn't exist, and the JSON body explains why.

Seeing a different error or a crash?

  • If you see a stack trace or the server stops, check that your GET /:id handler has the if (!task) check with return res.status(404).json(...) before calling res.json(task).
  • Make sure you're using parseInt(req.params.id) to convert the string parameter to a number for comparison.

Help me debug my 404 error handling in Express.

All five endpoints are working and your error handling is solid. You've just built and verified a fully functional REST API from scratch.

Secret mission

Filter Tasks by Completion Status

Real APIs don't just dump all their data at once. Add query parameter support so clients can filter tasks by completion status, just like a production to-do app would.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • The task-api project folder on your Desktop (contains server.js, routes/tasks.js, package.json, and node_modules/).
  • A running Node.js server process on port 3000.

Keep everything running

No action needed. Choose this if you're still actively building on your API or plan to extend it with more features.

  • Your server keeps running in the terminal. You can send requests to it anytime.
  • Your project files stay on your Desktop, ready to edit.

Pause - I'll come back to this later

Shut down the running server to free up port 3000, but keep your project files for later.

  • Switch to the terminal where your server is running.
  • Press Ctrl+C to stop the server.
  • Your task-api folder stays on your Desktop. When you're ready to resume, open a terminal in that folder and run node server.js again.

Delete - I don't want to use this again

Remove all project files and stop the server. This cannot be undone.

  • Switch to the terminal where your server is running and press Ctrl+C to stop it.
  • Delete the project folder by running the command for your operating system:

macOS

cd ~/Desktop
rm -rf task-api

Windows

cd $HOME\Desktop
Remove-Item -Recurse -Force task-api
  • Confirm the folder is gone by running ls (macOS) or dir (Windows) on your Desktop. You should no longer see task-api in the output.

Nice Work!

Nice Work!

You just built a fully functional REST API from scratch. Your task manager server handles real HTTP requests, routes them to the right handler, and returns structured JSON responses with proper status codes.

Here's what you learned:

  • Built a Node.js + Express server with JSON body-parsing middleware that listens for HTTP requests on port 3000.
  • Implemented full CRUD operations (Create, Read, Update, Delete) with route parameters and proper status codes like 201, 204, and 404.
  • Organized routes into modular files using express.Router() and tested every endpoint with curl commands from the terminal.
  • Secret Mission: Added query parameter filtering so clients can search tasks by completion status.

Ready to quiz yourself?