Route Methods (GET, POST, PUT, DELETE) · learncode.live

HTTP Methods & CRUD

Every HTTP request has a method (or verb) that tells the server what action to perform. Express maps these methods to CRUD operations:

HTTP MethodCRUDDescription
GETReadRetrieve a resource
POSTCreateCreate a new resource
PUTUpdate/ReplaceReplace an entire resource
PATCHUpdate/ModifyPartially modify a resource
DELETEDeleteRemove a resource
Client                          Server
  │                                │
  │──── GET /api/posts ──────────→│ Read all posts
  │←─── 200 [post1, post2, ...] ──│
  │                                │
  │──── POST /api/posts ─────────→│ Create post
  │←─── 201 { id: 3, ... } ──────│
  │                                │
  │──── PUT /api/posts/3 ────────→│ Replace post (full)
  │←─── 200 { id: 3, ... } ──────│
  │                                │
  │──── PATCH /api/posts/3 ──────→│ Modify post (partial)
  │←─── 200 { id: 3, ... } ──────│
  │                                │
  │──── DELETE /api/posts/3 ─────→│ Delete post
  │←─── 204 (no body) ───────────│
  │                                │

Defining Route Methods

Express provides a method for every HTTP verb on the app object:

const express = require('express');
const app = express();

// Parse JSON bodies (needed for POST/PUT/PATCH)
app.use(express.json());

// In-memory data store
let posts = [
  { id: 1, title: 'First Post', body: 'Hello world' },
  { id: 2, title: 'Second Post', body: 'Another post' }
];
let nextId = 3;

// GET - retrieve all posts
app.get('/api/posts', (req, res) => {
  res.json(posts);
});

// GET - retrieve a single post
app.get('/api/posts/:id', (req, res) => {
  const post = posts.find(p => p.id === parseInt(req.params.id));
  if (!post) return res.status(404).json({ error: 'Post not found' });
  res.json(post);
});

// POST - create a new post
app.post('/api/posts', (req, res) => {
  const { title, body } = req.body;
  if (!title || !body) {
    return res.status(400).json({ error: 'Title and body are required' });
  }
  const post = { id: nextId++, title, body };
  posts.push(post);
  res.status(201).json(post);
});

// PUT - replace an entire post
app.put('/api/posts/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const { title, body } = req.body;
  const index = posts.findIndex(p => p.id === id);

  if (index === -1) {
    return res.status(404).json({ error: 'Post not found' });
  }
  if (!title || !body) {
    return res.status(400).json({ error: 'Title and body are required' });
  }

  posts[index] = { id, title, body };
  res.json(posts[index]);
});

// PATCH - partially update a post
app.patch('/api/posts/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const post = posts.find(p => p.id === id);

  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }

  if (req.body.title !== undefined) post.title = req.body.title;
  if (req.body.body !== undefined) post.body = req.body.body;

  res.json(post);
});

// DELETE - remove a post
app.delete('/api/posts/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const index = posts.findIndex(p => p.id === id);

  if (index === -1) {
    return res.status(404).json({ error: 'Post not found' });
  }

  posts.splice(index, 1);
  res.status(204).send();
});

app.listen(3000);

Responding to Different Methods on the Same Path

Use app.route() to chain multiple methods on the same path:

app.route('/api/posts')
  .get((req, res) => {
    res.json(posts);
  })
  .post((req, res) => {
    const post = { id: nextId++, ...req.body };
    posts.push(post);
    res.status(201).json(post);
  });

app.route('/api/posts/:id')
  .get((req, res) => {
    const post = posts.find(p => p.id === parseInt(req.params.id));
    if (!post) return res.status(404).json({ error: 'Not found' });
    res.json(post);
  })
  .put((req, res) => {
    // replace logic
  })
  .patch((req, res) => {
    // partial update logic
  })
  .delete((req, res) => {
    // delete logic
  });

HEAD and OPTIONS

Express automatically handles HEAD (same as GET but no body) and OPTIONS (returns allowed methods) for routes you define:

// Explicit OPTIONS for custom behavior
app.options('/api/posts', (req, res) => {
  res.set('Allow', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
  res.status(204).send();
});

Method Override (for clients that only support GET/POST)

Some clients (older HTML forms) only support GET and POST. Use the method-override package:

npm install method-override
const methodOverride = require('method-override');

app.use(methodOverride('_method'));

// HTML form:
// <form action="/api/posts/1?_method=DELETE" method="POST">
//   <button>Delete</button>
// </form>

Useful HTTP Status Codes

CodeMeaningWhen to use
200 OKSuccessGET, PUT, PATCH
201 CreatedCreatedPOST (new resource)
204 No ContentSuccess, no bodyDELETE
400 Bad RequestClient errorInvalid input
401 UnauthorizedNot authenticatedMissing/invalid auth
403 ForbiddenNo permissionAuthenticated but not allowed
404 Not FoundResource not foundInvalid ID
409 ConflictDuplicate/conflictUnique constraint violation
422 UnprocessableValidation errorInvalid data format
500 Server ErrorInternal errorUnexpected server failure

Testing Routes with cURL

# GET all posts
curl http://localhost:3000/api/posts

# GET a single post
curl http://localhost:3000/api/posts/1

# POST a new post
curl -X POST http://localhost:3000/api/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"New Post","body":"Content here"}'

# PUT (replace)
curl -X PUT http://localhost:3000/api/posts/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated","body":"Updated content"}'

# PATCH (partial update)
curl -X PATCH http://localhost:3000/api/posts/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"Only title changed"}'

# DELETE
curl -X DELETE http://localhost:3000/api/posts/1

Key Takeaways

  • app.get(), app.post(), app.put(), app.patch(), app.delete() map to HTTP verbs
  • res.status(201).json(...) sets status and returns JSON
  • res.status(204).send() returns no content (common for DELETE)
  • app.route() chains multiple methods on the same path
  • Always validate input in POST/PUT/PATCH handlers
  • Use appropriate HTTP status codes for each response
Courses