Creating with POST
Part of the REST APIs section of Coddy's Node.js journey. Lesson 2 of 25.
A client that adds a task needs to know where the new task lives. It sends the new resource to the list's URL with POST, and the server answers 201 Created with the resource and a Location header naming its URL.
POST /api/events { "name": "Launch party" }
201 Created
Location: /api/events/12
{ "id": 12, "name": "Launch party" }The server picks the id, so two clients can never claim the same one. With the Location header the client can fetch the new event without building the URL itself. res.location(path) sets that header, and it chains like res.status:
res.status(201).location(`/api/events/${event.id}`).json(event);Challenge
EasyClients need a way to add tasks. Add a POST route that creates a task and tells the client where the new task lives.
- In
routes/tasks.js, add a route forPOST /api/tasks(inside the router, that path is'/'). It readstitlefrom the JSON body, for example{ "title": "Order new chairs" }. - Build the new task with four fields:
id: from thenextId()function instore.js. Import it next totasks.title: from the body.done:false.createdAt:new Date().toISOString(), the current time as text.
Add it to the
tasksarray, soGET /api/tasksshows it too. - Respond with status 201 and the new task as JSON. Set the
Locationheader to the task's own URL: for task 4 that is/api/tasks/4. On a fresh Run the response body looks like this (yourcreatedAtis the moment you send it):{ "id": 4, "title": "Order new chairs", "done": false, "createdAt": "2026-10-30T09:12:44.512Z" } - When the body has no
title, respond with status 400 and{ "error": "title is required" }, and add nothing to the list.
Try it: choose POST, path /api/tasks, body { "title": "Order new chairs" }. You should see 201 and the task above with id 4, and the response's Headers tab shows Location: /api/tasks/4. Send GET /api/tasks/4: you get the same task back. Then POST {} to /api/tasks: you get 400 with the error, and GET /api/tasks still lists 4 tasks, because the refused POST added nothing.
Try it yourself
import { Router } from 'express';
import { tasks } from '../store.js';
const router = Router();
router.get('/', (req, res) => {
res.json(tasks);
});
router.get('/:id', (req, res) => {
const task = tasks.find((t) => t.id === Number(req.params.id));
if (!task) {
return res.status(404).json({ error: 'Task not found' });
}
res.json(task);
});
export default router;
All lessons in REST APIs
1Resources and Verbs
Resources and URLsCreating with POST4Browsers, Versions and Config
What Is an OriginAllowing Another OriginCORSA Page That Calls the APIPractice on your own: Express playground