162 lines
8.1 KiB
Markdown
162 lines
8.1 KiB
Markdown
|
---
|
||
|
type: practical
|
||
|
---
|
||
|
- [Updating information](#Updating%20information)
|
||
|
- [The existence of the `PATCH` request](#The%20existence%20of%20the%20%60PATCH%60%20request)
|
||
|
- [Usage of `PUT`](#Usage%20of%20%60PUT%60)
|
||
|
- [Complete request example](#Complete%20request%20example)
|
||
|
- [Incomplete request example](#Incomplete%20request%20example)
|
||
|
- [Usage of `POST`](#Usage%20of%20%60POST%60)
|
||
|
- [General "style" remarks](#General%20%22style%22%20remarks)
|
||
|
- [Conclusion / TL;DR](#Conclusion%20/%20TL;DR)
|
||
|
|
||
|
## Updating information
|
||
|
Can we have optional fields in the update `PUT` and `POST` request?
|
||
|
|
||
|
### The existence of the `PATCH` request
|
||
|
>The **`PATCH`** HTTP method applies partial modifications to a resource.
|
||
|
>`PATCH` is somewhat analogous to the "update" concept found in [CRUD](https://developer.mozilla.org/en-US/docs/Glossary/CRUD) (in general, HTTP is different than [CRUD](https://developer.mozilla.org/en-US/docs/Glossary/CRUD), and the two should not be confused).[^1]
|
||
|
|
||
|
|
||
|
Hence, this definition also answers the question as to why we **should be careful with the usage of `PATCH`** if we consider it.
|
||
|
|
||
|
### Usage of `PUT`
|
||
|
>The PUT method requests that the state of the target resource be
|
||
|
created or replaced with the state **defined by the representation**
|
||
|
**enclosed in the request message payload**. [^3]
|
||
|
|
||
|
This implies that complete resource representation **is required** (all the fields), as Jackson(Spring) will reset the missing ones to their default values (e.g. int = 0, boolean = false, String = null, etc.)
|
||
|
|
||
|
|
||
|
#### Complete request example
|
||
|
|
||
|
*Request:*
|
||
|
```json
|
||
|
{
|
||
|
"isbn": "978-3-16-148410-0",
|
||
|
"name": "Among us story",
|
||
|
"author": "Yo mama",
|
||
|
"genre": "Horror",
|
||
|
"publisher": "Team 22",
|
||
|
"publishDate": "2023-09-01",
|
||
|
"pages": 320
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
*Response:*
|
||
|
```json
|
||
|
<<< 200 OK
|
||
|
{
|
||
|
"isbn": "978-3-16-148410-0",
|
||
|
"name": "Among us story",
|
||
|
"author": "Yo mama",
|
||
|
"genre": "Horror",
|
||
|
"publisher": "Team 22",
|
||
|
"publishDate": "2023-09-01",
|
||
|
"pages": 320
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
#### Incomplete request example
|
||
|
(this is assuming the above request happened already)
|
||
|
*Request:*
|
||
|
```json
|
||
|
{
|
||
|
"isbn": "978-3-16-148410-0",
|
||
|
"name": "Among us story",
|
||
|
|
||
|
|
||
|
"publisher": "Team 22",
|
||
|
|
||
|
"pages": 320
|
||
|
}
|
||
|
```
|
||
|
|
||
|
*Response*:
|
||
|
```json
|
||
|
|
||
|
<<< 200 OK
|
||
|
{
|
||
|
"isbn": "978-3-16-148410-0",
|
||
|
"name": "Among us story",
|
||
|
"author": null,
|
||
|
"genre": null,
|
||
|
"publisher": "Team 22",
|
||
|
"publishDate": null,
|
||
|
"pages": 320
|
||
|
}
|
||
|
```
|
||
|
### Usage of `POST`
|
||
|
Contrary to `PUT`:
|
||
|
>The `POST` method is **used to request that the target resource process the enclosed representation according to the resource's own specific semantics**. The meaning of a `POST` request is determined by the server and is usually dependent on the resource identified by the Request-URI.[^4]
|
||
|
|
||
|
Therefore we *can* send requests which contain data that are non-complete resource representations. We do have to explain this to the other team tho...
|
||
|
|
||
|
i.e.
|
||
|
*Request:*
|
||
|
```json
|
||
|
{
|
||
|
"isbn": "978-3-16-148410-0"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
*Response:*
|
||
|
``` json
|
||
|
<<< 201 Created
|
||
|
{
|
||
|
"isbn": "978-3-16-148410-0",
|
||
|
"name": null,
|
||
|
"author": null,
|
||
|
"genre": null,
|
||
|
"publisher": null,
|
||
|
"publishDate": null,
|
||
|
"pages": 0
|
||
|
}
|
||
|
|
||
|
|
||
|
```
|
||
|
|
||
|
## General "style" remarks
|
||
|
(based on the API conventions + CRUD principle)
|
||
|
>The base URL should be neat, elegant, and simple so that developers using your product can easily use them in their web applications. A long and difficult-to-read base URL is not just bad to look at, but can also be prone to mistakes when trying to recode it. Nouns should always be trusted.[^2]
|
||
|
|
||
|
Hence, using a verb in `POST /api/v1/book/create` and `PUT /api/v1/book/update` is not ideal.
|
||
|
|
||
|
Table[^2] of common conventions for the usage of HTTP requests:
|
||
|
|
||
|
|
||
|
|
||
|
| **Resource** | `POST` | `GET` | `PUT` | `DELETE` |
|
||
|
| ------------------- | --------------------------------- | ----------------------------------- | --------------------------------------------- | -------------------------------- |
|
||
|
| /customers | Create a new customer | Retrieve all customers | Bulk update of customers | Remove all customers |
|
||
|
| /customers/1 | Error | Retrieve the details for customer 1 | Update the details of customer 1 if it exists | Remove customer 1 |
|
||
|
| /customers/1/orders | Create a new order for customer 1 | Retrieve all orders for customer 1 | Bulk update of orders for customer 1 | Remove all orders for customer 1 |
|
||
|
|
||
|
Hence, in our case:
|
||
|
|
||
|
| **Resource** | `POST` | `GET` | `PUT` | `DELETE` |
|
||
|
| ------------- | ----------------- | -------------------------------------------------------- | ------------------------------------------------ | ----------------------- |
|
||
|
| /books | Create a new book | Retrieve all books (+ optional parameters for filtering) | 405 | Remove all books |
|
||
|
| /books/{isbn} | 405 | Retrieve the details for book with `isbn` | Update the details of book with `isbn` if exists | Remove book with `isbn` |
|
||
|
|
||
|
|
||
|
|
||
|
## Conclusion / TL;DR
|
||
|
Given the information provided above, I propose we change the current specification as follows:
|
||
|
|
||
|
| Method | Old URL/Description | New URL/Description | Reasoning |
|
||
|
| -------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||
|
| `POST` | `/api/v1/book/create` - Create book - pass JSON body (isbn is required) | `/api/v1/books` - Create a book - pass JSON body (isbn is required) | Use resource-based naming convention (`books` as a collection). HTTP POST implies creation; no need for `/create`. |
|
||
|
| `PUT` | `/api/v1/book/update` - Update book - pass JSON body (isbn is required) | `/api/v1/books/{isbn}` - Update a specific book - pass JSON body | Use path parameter (`{isbn}`) to specify which book to update. HTTP PUT implies updating a specific resource. |
|
||
|
| `GET` | `/api/v1/book?genre={genre}&author={author}&data="csv"` - Get book(s) by property in csv/json format | `/api/v1/books?genre={genre}&author={author}` - Get books by property. Use `Accept` header for csv/json format | Use plural `books` for collections. Use HTTP `Accept` header for content negotiation (CSV/JSON), keeping URLs clean. |
|
||
|
| `DELETE` | `/api/v1/book?genre={genre}&author={author}` - Delete book(s) by property | `/api/v1/books` - Delete books - pass filter criteria in body or delete one by `/api/v1/books/{isbn}` | `DELETE` requests with body for filtering criteria, or use path param for deleting a single resource for better clarity. |
|
||
|
| `POST` | `/api/v1/book/import?data="csv"` - Import books from csv/json file (isbn is required for each) | `/api/v1/books/import` - Import books from CSV/JSON file - pass file in body; use `Content-Type` header | Use resource-based naming (`books/import`). The `Content-Type` header indicates file type (CSV/JSON); keep query params clean. |
|
||
|
|
||
|
|
||
|
|
||
|
[^1]: [Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH)
|
||
|
[^2]: [Swagger](https://swagger.io/resources/articles/best-practices-in-api-design/)
|
||
|
[^3]: [RFC 7231 - PUT](https://datatracker.ietf.org/doc/html/rfc7231#autoid-37)
|
||
|
[^4]: [RFC 7231 - POST](https://datatracker.ietf.org/doc/html/rfc7231#autoid-36)
|