---
html_meta:
"description": "plone.restapi not only exposes content objects via a RESTful API, but the API consumer can create, read, update, and delete a content object."
"property=og:description": "plone.restapi not only exposes content objects via a RESTful API, but the API consumer can create, read, update, and delete a content object."
"property=og:title": "Content Manipulation"
"keywords": "Plone, plone.restapi, REST, API, Content, Manipulation"
---
# Content Manipulation
`plone.restapi` does not only expose content objects via a RESTful API.
The API consumer can create, read, update, and delete a content object.
Those operations can be mapped to the HTTP verbs `POST` (Create), `GET` (Read), `PUT` (Update) and `DELETE` (Delete).
Manipulating resources across the network using HTTP as an application protocol is one of core principles of the REST architectural pattern.
This allows us to interact with a specific resource in a standardized way.
| Verb | URL | Action |
| -------- | ------------------------ | ----------------------------------------- |
| `POST` | `/folder` | Creates a new document within the folder |
| `GET` | `/folder/\{document-id}` | Request the current state of the document |
| `PATCH` | `/folder/\{document-id}` | Update the document details |
| `DELETE` | `/folder/\{document-id}` | Remove the document |
## Creating a Resource with `POST`
To create a new resource, we send a `POST` request to the resource container.
If we want to create a new document within an existing folder, we send a `POST` request to that folder:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_post.req
```
By setting the `Accept` header, we tell the server that we would like to receive the response in the `application/json` representation format.
The `Content-Type` header indicates that the body uses the `application/json` format.
The request body contains the minimal necessary information needed to create a document (the type and the title).
You could set other properties, such as `description`, as well.
A special property during content creation is `UID`
It requires the user to have the `Manage Portal` permission to set it.
Without the permission, the request will fail as `Unauthorized`.
### Successful Response (201 Created)
If a resource has been created, the server responds with the {term}`201 Created` status code.
The `Location` header contains the URL of the newly created resource, and the resource representation is in the payload:
```{literalinclude} ../../src/plone/restapi/tests/http-examples/content_post.resp
:language: http
```
### Unsuccessful Response (400 Bad Request)
If the resource could not be created, for instance because the title was missing in the request, the server responds with {term}`400 Bad Request`:
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
'message': 'Required title field is missing'
}
```
The response body can contain information about why the request failed.
### Unsuccessful Response (500 Internal Server Error)
If the server can not properly process a request, it responds with {term}`500 Internal Server Error`:
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
'message': 'Internal Server Error'
}
```
The response body can contain additional information, such as an error trace or a link to the documentation.
### Possible `POST` Responses
Possible server responses for a `POST` request are:
- {term}`201 Created` (Resource has been created successfully)
- {term}`400 Bad Request` (malformed request to the service)
- {term}`500 Internal Server Error` (server fault, can not recover internally)
### `POST` Implementation
A pseudo-code example of the `POST` implementation on the server:
```python
try:
order = createOrder()
if order == None:
# Bad Request
response.setStatus(400)
else:
# Created
response.setStatus(201)
except:
# Internal Server Error
response.setStatus(500)
```
```{todo}
Link to the real implementation...
```
## Reading a Resource with `GET`
After a successful `POST`, we can access the resource by sending a `GET` request to the resource URL:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_get.req
```
### Successful Response (200 OK)
If a resource has been retrieved successfully, the server responds with {term}`200 OK`:
```{literalinclude} ../../src/plone/restapi/tests/http-examples/content_get.resp
:language: http
```
For folderish types, their children are automatically included in the response as `items`.
To disable the inclusion, add the `GET` parameter `include_items=false` to the URL.
By default, only basic metadata is included.
To include additional metadata, you can specify the names of the properties with the `metadata_fields` parameter.
See also {ref}`retrieving-additional-metadata`.
The following example additionally retrieves the `UID` and `Creator`:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_get_folder.req
```
```{literalinclude} ../../src/plone/restapi/tests/http-examples/content_get_folder.resp
:language: http
```
```{note}
For folderish types, collections or search results, the results will be **batched** if the size of the resultset exceeds the batch size.
See {doc}`batching` for more details on how to work with batched results.
```
### Unsuccessful response (404 Not Found)
If a resource cannot be found, the server will respond with {term}`404 Not Found`:
```
HTTP/1.1 404 Not Found
Content-Type: application/json
{
'error': 'NotFound'
}
```
### `GET` Implementation
A pseudo-code example of the `GET` implementation on the server:
```python
try:
order = getOrder()
if order == None:
# Not Found
response.setStatus(404)
else:
# OK
response.setStatus(200)
except:
# Internal Server Error
response.setStatus(500)
```
You can find implementation details in the [plone.restapi.services.content.add.FolderPost class](https://github.com/plone/plone.restapi/blob/dde57b88e0f1b5f5e9f04e6a21865bc0dde55b1c/src/plone/restapi/services/content/add.py#L35-L61).
### `GET` Responses
Possible server responses for a `GET` request are:
- {term}`200 OK`
- {term}`404 Not Found`
- {term}`500 Internal Server Error`
## Updating a Resource with `PATCH`
To update an existing resource, we send a `PATCH` request to the server.
`PATCH` allows providing just a subset of the resource, such as the values you actually want to change.
If you send the value `null` for a field, the field's content will be deleted, and the `missing_value` defined for the field in the schema will be set.
Note that this is not possible if the field is `required`, and it only works for Dexterity types, not Archetypes:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_patch.req
```
### Successful Response (204 No Content)
A successful response to a `PATCH` request will be indicated by a {term}`204 No Content` response by default:
```{literalinclude} ../../src/plone/restapi/tests/http-examples/content_patch.resp
:language: http
```
### Successful Response (200 OK)
You can get the object representation by adding a `Prefer` header with a value of `return=representation` to the `PATCH` request.
In this case, the response will be a {term}`200 OK`:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_patch_representation.req
```
```{literalinclude} ../../src/plone/restapi/tests/http-examples/content_patch_representation.resp
:language: http
```
See the full specifications in [RFC 5789: `PATCH` Method for HTTP](https://datatracker.ietf.org/doc/html/rfc5789).
## Replacing a Resource with `PUT`
```{note}
`PUT` is not implemented yet.
```
To replace an existing resource, we send a `PUT` request to the server:
```{todo}
Add example.
```
In accordance with the HTTP specification, a successful `PUT` will not create a new resource or produce a new URL.
`PUT` expects the entire resource representation to be supplied to the server, rather than just changes to the resource state.
This is usually not a problem since the consumer application requested the resource representation before a `PUT` anyways.
When the `PUT` request is accepted and processed by the service, the consumer will receive a {term}`204 No Content` response ({term}`200 OK` would be a valid alternative).
### Successful Update (204 No Content)
When a resource has been updated successfully, the server sends a {term}`204 No Content` response:
```{todo}
Add example.
```
### Unsuccessful Update (409 Conflict)
Sometimes requests fail due to incompatible changes.
The response body includes additional information about the problem:
```{todo}
Add example.
```
### `PUT` Implementation
A pseudo-code example of the `PUT` implementation on the server:
```python
try:
order = getOrder()
if order:
try:
saveOrder()
except conflict:
response.setStatus(409)
# OK
response.setStatus(200)
else:
# Not Found
response.setStatus(404)
except:
# Internal Server Error
response.setStatus(500)
```
```{todo}
Link to the real implementation...
```
### `PUT` Responses
Possible server responses for a `PUT` request are:
- {term}`200 OK`
- {term}`404 Not Found`
- {term}`409 Conflict`
- {term}`500 Internal Server Error`
### `POST` vs. `PUT`
Using `POST` or `PUT` depend on the desired outcome.
- Use `POST` to create a resource identified by a service-generated URI.
- Use `POST` to append a resource to a collection identified by a service-generated URI.
- Use `PUT` to overwrite a resource.
This follows [RFC 7231: HTTP 1.1: `PUT` Method](https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.4).
## Removing a Resource with `DELETE`
We can delete an existing resource by sending a `DELETE` request:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_delete.req
```
A successful response will be indicated by a {term}`204 No Content` response:
```{literalinclude} ../../src/plone/restapi/tests/http-examples/content_delete.resp
:language: http
```
### `DELETE` Implementation
A pseudo-code example of the `DELETE` implementation on the server:
```python
try:
order = getOrder()
if order:
if can_delete(order):
# No Content
response.setStatus(204)
else:
# Not Allowed
response.setStatus(405)
else:
# Not Found
response.setStatus(404)
except:
# Internal Server Error
response.setStatus(500)
```
```{todo}
Link to the real implementation...
```
### `DELETE` Responses
Possible responses to a `DELETE` request are:
- {term}`204 No Content`
- {term}`404 Not Found` (if the resource does not exist)
- {term}`405 Method Not Allowed` (if deleting the resource is not allowed)
- {term}`500 Internal Server Error`
## Reordering sub resources
The resources contained within a resource can be reordered using the `ordering` key with a `PATCH` request on the container.
Use the `obj_id` subkey to specify which resource to reorder.
The subkey `delta` can be `top`, `bottom`, or a negative or positive integer for moving up or down.
Reordering resources within a subset of resources can be done using the `subset_ids` subkey.
A response of `400 BadRequest` with a message `Client/server ordering mismatch` will be returned if the value is not in the same order as server side.
A response of `400 BadRequest` with a message `Content ordering is not supported by this resource` will be returned if the container does not support ordering:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_reorder.req
```
To rearrange all items in a folderish context, use the `sort` key.
The `on` subkey defines the catalog index to be sorted on.
The `order` subkey indicates either the `ascending` or `descending` order of items.
A response `400 BadRequest` with a message `Content ordering is not supported by this resource` will be returned if the container does not support ordering:
```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../src/plone/restapi/tests/http-examples/content_resort.req
```