We type it multiple times every day, we call it "a link" or "a web address", yet how many of us actually remember why it is called a URL? And why is it important for a good REST API?
URLs (Uniform Resource Locators) are a subset of URIs (Uniform Resource Identifiers). There is another subset of URIs, the URNs (Uniform Resource Names). All URIs identify resources that are the basic building stones of REST.
A URL (unlike a URN), besides being a unique identifier, contains information about how to locate and access the resource. The URL https://dastalvi.com/contact tells a client to
- use the HTTP and TLS protocols (https),
- connect to the server with the domain name dastalvi.com
- and pass the path /contact to the server; the path is used to locate the resource within the server.
When creating something new, including an API, we often start by copying an example of something we know works. The Petstore sample API from Swagger is a famous example that we might want to use as a starting point, especially if we want to create an OpenAPI specification for our API.
However, not every part of even a well-known example is good to copy without thinking if it fits into our context. Let's take a closer look at the operation to update a pet using the PUT method. Can you see something dangerous?
Clearly, the specification reuses the same Pet schema containing all fields of a pet including "id" for PUT /pet, POST /pet and GET /pet/{petId}
Reuse is a good thing as it eliminates duplicates, right? But not all duplicates are good to be removed. Trying to be too clever and use the "id" from the PUT payload to identify the pet to be updated and removing it from the URL takes away an important feature of our URL: that it should be a URI, a unique identifier of the resource!
The PUT method should be idempotent and it should replace the whole resource (identified by the URI) by the content provided in the request. The resource it modifies is the one we can retrieve by GET /pet/{petId}, where we clearly see that the "id" is included in the URL (because the GET method does not have a request body).
Probably a more realistic approach is described in the API guidelines from Microsoft. It admits that there is usually a collection of entities (pets in our case) we work with.
The "Changing collections" section shows us that the PUT method should indicate which collection item we want to update.
On the other hand, if we want to create a new item of the collection and let the server generate the new "id", we should use the POST method. Because sending multiple such requests will create multiple IDs, so this is exactly the situation where we need the non-idempotent POST.