This is the 7th day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021.
- Why Should You Use a PUT Request Instead of a POST Request
- Dieter Jordens
- The Nuggets translation Project
I’ve been asked several times: Why use a PUT request instead of a POST request?
You will understand why after reading this article. You’ll be able to implement the PUT method in Python using FastAPI and learn how to test it properly.
Difference between A PUT request and a POST request
Let’s start by explaining the key feature of PUT requests: idempotency.
If you call the PUT request multiple times, the result remains the same. So, suppose you create a resource: a book. If you call this method twice, the result is still the same. More precisely, a book exists only once in a database. Every book is unique.
A POST request is a little different. If you create a book and then create the same book, you will have two books. In other words, you’ll get a different result the second time you ask.
Both approaches have their merits, but the PUT method is very good at preventing side effects.
To give you a clearer idea, let me describe a scenario: Juliette wants to buy a sandwich. She clicks a button. As the store was a bit slow to respond, she pressed the same button again. The same request reaches the server twice. Because you already implemented this method using the PUT request, it doesn’t have any impact on the second visit to the server. Juliette doesn’t have to eat two meals, and she saves a lot of money. Great, right?
In many cases, PUT requests are well suited for preventing unnecessary updates.
Note: Note that even if you use the PUT method, you may still violate idempotency. If you implement the PUT method in the same way as the POST method, there will be no difference between the two. This violates the principle of PUT. As an API developer, it is your responsibility to create a valid PUT request.
How do I create a PUT request
First, set up your Python environment. To do this, you’ll need libraries: FastAPI, Uvicorn, Pydantic, and Requests. You can install it using any tool — my personal favorite is using virtual environments.
In this article, you will build the PUT request from scratch. We will start with a collection of empty books and create new ones. The data types we use in this tutorial are very simple:
class Book:
isbn: int
title: str
Copy the code
Let’s try to keep things simple so we don’t have to use the database at this point. Dictionaries are great for updating data, and we’ll use them instead of databases.
Your PUT request will create a new book. If the book does not exist, it will create a new one in the dictionary. If the book exists, it will replace the original book. PUT requests that we be allowed to update the entire object one at a time.
In FastAPI, the code structure looks like this (depending on your underlying data structure) :
import fastapi
import uvicorn
from book import Book
app = fastapi.FastAPI()
books: dict[int, Book] = {}
@app.put("/books/")
def create_or_update_book(book: Book) -> Book:
books[book.isbn] = book
Copy the code
Now, let’s review what happened here. If the book already exists, we replace the original book. If the book does not exist, the dictionary will have one more book after execution. Finally, this method returns the Book object newly added to the dictionary.
Great, isn’t it?
The main difference between the PUT method and the POST method is how the same resource is updated. If you want to do the same update with the POST method, you should throw an exception when the key already exists in the dictionary.
If your data structure allows duplicate values, the POST method will not throw an exception. Every time you call the POST method, you create a new book. This also makes sense in some situations, such as when selling books.
Testing the PUT request
FastAPI makes writing unit and integration tests easy. Starting a test is always difficult, especially with newer frameworks. But as you become more familiar with FastAPI, you can also do TDD perfectly.
The first test is often the most important one. When you create a book that does not exist, you will get a new book in your collection. The code is as follows:
@mock.patch('main.books'{},)
def test_givenBook_whenCreateOrUpdateBook_thenBookIsAddedToCollection(self) - >None:
expected_book = book_clean_code
actual_book = create_or_update_book(book_clean_code)
self.assertEqual(expected_book, actual_book)
Copy the code
Since we’re using a dictionary, we can’t allow the same element to appear twice. This means that there is no point in writing a test for this situation. There’s another case to consider here. If you update an existing book, you should end up with a new one, as follows:
@mock.patch('main.books', {9780132350884: book_clean_code})
def test_givenUpdatedBook_whenCreateOrUpdateBook_thenBookIsUpdated(self) - >None:
expected_book = Book(isbn=9780132350884, title='Cleaner Code')
actual_book = create_or_update_book(expected_book)
self.assertEqual(expected_book, actual_book)
Copy the code
Finally, we can also write integration tests. The most important part of the integration test is to see if we get the right 200 OK response. This test is the same as calling our API using Postman, but better yet, you can automate such tests. The code is as follows:
class TestIntegrationMain(TestCase) :
def setUp(self) - >None:
self.client = TestClient(app)
@mock.patch('main.books'{},)
def test_integration_givenValidBook_whenCreateOrUpdateBook_thenReturnCreatedBook(self) - >None:
valid_book = {
'isbn': 9780132350884.'title': 'Clean Code: A Handbook of Agile Software Craftsmanship'
}
response = self.client.put('/books/', json=valid_book)
self.assertEqual(200, response.status_code)
self.assertEqual(valid_book, response.json())
Copy the code
conclusion
After reading this article, you can see that the PUT method creates a stable API. Now you know what idempotent is, and I’m sure you can implement your PUT method yourself.
FastAPI makes it easy to create PUT requests, and it makes testing very easy. This framework is hard to beat in terms of simplicity. What? Do you still want to learn FastAPI? My previous article on GET, POST, and DELETE methods might be helpful, so check it out
Thanks for reading this article!