HTTP Methods and CRUD

Four things you do to data, and the verbs that mean them

Advanced 9 min

In this lesson

Almost everything an application does to data is one of four things: create it, read it, update it, or delete it. That is CRUD, and HTTP has a verb for each. Learning the convention means you can guess how an unfamiliar API works, and design one other people can guess too.

Explain it like I’m 5

CRUD is the short list of things any app does with information: make it, look at it, change it, bin it. The HTTP methods are just the words for those four.

The four operations and their verbs

Each CRUD operation has a matching method:

  • Create → POST, add something new.
  • Read → GET, fetch without changing anything.
  • Update → PUT or PATCH, PUT replaces the whole record, PATCH changes some fields.
  • Delete → DELETE, remove it.

The important property of GET is that it changes nothing. Browsers, caches, and search crawlers all assume this and will happily repeat a GET. Anything that modifies data must not be one.

Naming routes so they read predictably

The convention is that the URL names the thing and the method says what to do with it. Nouns in the path, verbs in the method. That is why the same path appears more than once in a well-designed API.

Example
GET    /api/books        list every book
POST   /api/books        create a new book
GET    /api/books/7      read book 7
PUT    /api/books/7      replace book 7
PATCH  /api/books/7      change some fields of book 7
DELETE /api/books/7      delete book 7
Two paths, six operations. The method carries the verb.

Fill in the table for a book tracker: give each CRUD action its method and path. Use /api/books for the collection and /api/books/1 for a single book.

def endpoint(action):
    table = {
        "create": ("POST", "/api/books"),
        # TODO: add read, update, and delete
    }
    return table[action]

for action in ["create", "read", "update", "delete"]:
    method, path = endpoint(action)
    print(f"{action}: {method} {path}")

Answer with the right status code

The status code is part of your answer, not decoration. A handful covers nearly everything:

  • 200 OK, the read or update worked.
  • 201 Created, a POST made something new.
  • 204 No Content, it worked and there is nothing to send back, typical after DELETE.
  • 400 Bad Request, the caller sent something invalid.
  • 404 Not Found, no such record.

Returning 200 with {"error": "not found"} in the body is a common and unhelpful habit: it tells every automated caller that the request succeeded.

Example · app.py
@app.route("/api/books/<int:book_id>", methods=["DELETE"])
def delete_book(book_id):
    book = find_book(book_id)
    if book is None:
        return jsonify({"error": "book not found"}), 404
    BOOKS.remove(book)
    return "", 204
404 when it isn't there, 204 when it's gone.

Common mistake: Using GET for something that changes data

Why it happens:

A link is the easiest thing to build, and GET is what links do.

How to fix it:

Use POST, PUT, PATCH, or DELETE for anything that modifies. GET must be safe to repeat, because things you don’t control will repeat it.

Common mistake: Putting the verb in the URL

Why it happens:

/api/createBook reads clearly to a human writing it.

How to fix it:

Let the method be the verb and the path be the noun: POST /api/books. It keeps the API predictable and stops the route list doubling in size.

Common mistake: Returning 200 for every response

Why it happens:

The endpoint ran without crashing, so it feels like a success.

How to fix it:

Match the code to the outcome: 201 after creating, 204 after deleting, 400 for bad input, 404 when it doesn’t exist. Callers branch on the code, not on your error text.

Which method should add a new record?

What is the difference between PUT and PATCH?

A DELETE succeeds and there is nothing to return. Which status fits best?

Mini exercise (medium)

Write route_for(action, item_id=None) for a book API. It should return a (method, path) tuple following the convention: collection actions use /api/books, single-item actions use /api/books/<id>, and the method comes from the CRUD operation.

Now you. Edit the starter code below, then Run it, everything happens in the browser.

def route_for(action, item_id=None):
    return ("GET", "/api/books")  # TODO: use the action and item_id

print(route_for("create"))
print(route_for("read", 7))
print(route_for("delete", 7))

What to learn next

You learned the convention that makes APIs predictable: nouns in the path, verbs in the method. GET reads and must never change anything, POST creates, PUT replaces, PATCH edits, DELETE removes, and the status code carries real meaning: 201 after creating, 204 after deleting, 400 and 404 when the caller got it wrong.

One thing is still missing: most real APIs want to know who is asking. API Keys, Secrets, and Safe Configuration covers keys, environment variables, and rate limits.