---
myst:
  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

{
  "message": "Resource not found: https://<resource url>",
  "type": "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
```
