My Docs
DeploymentTrelloCalendar 🗓 Family Promise Roadmap
LAMBDA_LABS_Family_Promise
LAMBDA_LABS_Family_Promise
  • Home
  • navigation
    • Resume
    • My Notes:
    • NAVIGATION
    • Calendar
    • Youtube:
    • Roadmap:
    • TEAM MEMBERS
    • Running List Of Notes Links & Pertinent Info From Meetings
    • Trello
      • Github/Trello Integration
  • UX
    • UX_TOPICS
      • Action Items:
      • Accessibility
      • Figma Notes
        • Tables In Figma
        • Notes
        • Frames in Figma
        • Prototyping In Figma
        • More Notes
      • UX-Design
        • Facebook Graph API
      • Ant Design
        • ANT Components
          • Buttons
        • ANT DOCS
        • Application (Codesandbox)
      • Examples
      • How to add external URL links to your prototype
  • CANVAS
    • Interview
    • Design
      • What's Inclusive Design?
      • Accessibility
      • What are Design Systems?
    • Canvas
      • Career Readiness:
    • Notes
      • User Experience Design
      • User Research
      • Interaction Design
    • UX-Engineer
      • Accessibility
      • Patterns
      • Design Tools
      • UX Principles
      • Design Critiques
      • Product Review
      • Quiz
      • Seven Principles of Design
      • Other Articles
    • Labs
  • Front End
    • Frontend:
    • Redux
  • Back End
    • Backend:
      • API
  • Research
    • Research Navigation
      • Front End
      • Back End
      • UX
      • PTM
      • General
  • DS_API
    • Data Science API
  • ROLES
    • TEAM ROLES
      • Bryan Guner
  • Action Items
    • Trello
    • Maps
  • ARCHITECTURE
    • DNS
    • AWS
    • Heroku
  • Questions
    • From Previous Cohort
  • Standup Notes
    • Meeting Notes
      • Stakeholder Meeting 1
      • 9/29/2021
  • GitHub & Project Practice
    • GitHub
      • Github Guide
      • Github Actions:
      • Live Implementation
  • MISC
    • MISCELLANEOUS
      • Links
  • Background Information
    • Background Info
      • Swagger OPEN API SPECIFICATION
        • Swagger Docs (General)
      • GITHUB:
        • Git Bash
        • Git Prune:
  • DOCS
    • DS AP
    • What is JSON Web Token?
      • Environment Variables
      • Git Rebase:
      • Git Workflow:
      • Linting and Formatting
    • Project Docs
      • Eng-Docs-Home
      • Basic Node API
      • Contributing to this scaffold project
      • Examples:
    • PROJECT DESCRIPTION (Feature List)
    • Labs Learners Guide
    • REACT
      • Create React App
      • Awesome React
    • Labs Engineering Docs
      • Okta Basics
      • Roadmap
      • Repositories
  • Workflow
    • Workflow
    • Advice
  • AWS
    • AWS
      • Elastic Beanstalk
        • Elastic Beanstalk DNS
      • Amplify:
        • Amplify-DNS
    • Account Basics
    • AWS-Networking
  • Career & Job Hunt
    • Career
  • LABS
    • Introduction
    • User Stories
    • Why Pairing?
    • GitHub
    • Planning as an Engineer
    • Authentication and Authorization
      • Authentication VS Authorization
    • Giving Feedback
    • Modules Grades Understanding Your ISA
    • Rest Architecture
Powered by GitBook
On this page
  • Introduction
  • Explore the API using Postman
  • Configuration
  • Usage
  • Content Manipulation
  • Creating a Resource with POST
  • Reading a Resource with GET
  • Updating a Resource with PATCH
  • Replacing a Resource with PUT
  • Removing a Resource with DELETE
  • Reordering sub resources
  • Conventions
  • Naming Convention for REST API Resources/Endpoints
  • Naming Convention for attribute names in URIs
  • Naming Convention for attribute names in response body
  • Versioning

Was this helpful?

  1. LABS

Rest Architecture

PreviousModules Grades Understanding Your ISA

Last updated 3 years ago

Was this helpful?

Introduction

API Browser Quick Guide

It can make your life easier if you use some kind of API browser application to explore the API when diving into this documentation.

  • We recommend to use the free browser plugin.

  • For easy onboarding take a look at our Quick-Guide.

A hypermedia API provides an entry point to the API, which contains hyperlinks the clients can follow. Just like a human user of a regular website, who knows the initial URL of a website and then follows hyperlinks to navigate through the site. This has the advantage that the client only needs to understand how to detect and follow links. The URLs (apart from the inital entry point) and other details of the API can change without breaking the client.

The entry point to the Plone RESTful API is the portal root. The client can ask for a API response by setting the 'Accept' HTTP header to 'application/json':

http

curl

httpie

python-requests

GET /plone HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

This uses so-called ‘content negotiation’

The server will then respond with the portal root in the JSON format:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@components": {
        "actions": {
            "@id": "http://localhost:55001/plone/@actions"
        },
        "breadcrumbs": {
            "@id": "http://localhost:55001/plone/@breadcrumbs"
        },
        "contextnavigation": {
            "@id": "http://localhost:55001/plone/@contextnavigation"
        },
        "navigation": {
            "@id": "http://localhost:55001/plone/@navigation"
        }
    },
    "@id": "http://localhost:55001/plone",
    "@type": "Plone Site",
    "blocks": {},
    "blocks_layout": {},
    "description": "",
    "id": "plone",
    "is_folderish": true,
    "items": [
        {
            "@id": "http://localhost:55001/plone/front-page",
            "@type": "Document",
            "description": "Congratulations! You have successfully installed Plone.",
            "review_state": "private",
            "title": "Welcome to Plone"
        }
    ],
    "items_total": 1,
    "parent": {},
    "title": "Plone site"
}

@id is a unique identifier for resources (IRIs). The @id property can be used to navigate through the web API by following the links.

@type sets the data type of a node or typed value

items is a list that contains all objects within that resource.

A client application can “follow” the links (by calling the @id property) to other resources. This allows to build a losely coupled client that does not break if some of the URLs change, only the entry point of the entire API (in our case the portal root) needs to be known in advance.

Another example, this time showing a request and response for a document. Click on the buttons below to show the different syntaxes for the request.

http

curl

httpie

python-requests

GET /plone/front-page HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
HTTP/1.1 200 OK
Content-Type: application/json

{
    "@components": {
        "actions": {
            "@id": "http://localhost:55001/plone/front-page/@actions"
        },
        "breadcrumbs": {
            "@id": "http://localhost:55001/plone/front-page/@breadcrumbs"
        },
        "contextnavigation": {
            "@id": "http://localhost:55001/plone/front-page/@contextnavigation"
        },
        "navigation": {
            "@id": "http://localhost:55001/plone/front-page/@navigation"
        },
        "types": {
            "@id": "http://localhost:55001/plone/front-page/@types"
        },
        "workflow": {
            "@id": "http://localhost:55001/plone/front-page/@workflow"
        }
    },
    "@id": "http://localhost:55001/plone/front-page",
    "@type": "Document",
    "UID": "SomeUUID000000000000000000000001",
    "allow_discussion": false,
    "changeNote": "",
    "contributors": [],
    "created": "1995-07-31T13:45:00",
    "creators": [
        "test_user_1_"
    ],
    "description": "Congratulations! You have successfully installed Plone.",
    "effective": null,
    "exclude_from_nav": false,
    "expires": null,
    "id": "front-page",
    "is_folderish": false,
    "language": "",
    "layout": "document_view",
    "lock": {
        "locked": false,
        "stealable": true
    },
    "modified": "1995-07-31T17:30:00",
    "next_item": {},
    "parent": {
        "@id": "http://localhost:55001/plone",
        "@type": "Plone Site",
        "description": "",
        "title": "Plone site"
    },
    "previous_item": {},
    "relatedItems": [],
    "review_state": "private",
    "rights": "",
    "subjects": [],
    "table_of_contents": null,
    "text": {
        "content-type": "text/plain",
        "data": "<p>If you&#x27;re seeing this instead of the web site you were expecting, the owner of this web site has just installed Plone. Do not contact the Plone Team or the Plone mailing lists about this.</p>",
        "encoding": "utf-8"
    },
    "title": "Welcome to Plone",
    "version": "current",
    "versioning_enabled": true,
    "working_copy": null,
    "working_copy_of": null
}

And so on, see

Explore the API using Postman

Note

The Chrome-Extension version of Postman is deprecated and it is recommended to use the native app available instead.

Configuration

To easily follow links returned by request based on the API,

  • go to the menu under the wrench icon on the top right

  • choose Settings

  • activate the option Retain headers on clicking on links by selecting ON:

The option Send anonymous usage data to Postman should be deactivated by setting to OFF.

Usage

Now set the appropriate HTTP headers.


You have to select

  • A valid existing user with appropriate permissions


The request is now ready and can be send by clicking on Send button.

Conclusion

You can now explore the whole stucture of your application easily via the API using GET requests.

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

http

curl

httpie

python-requests

POST /plone/folder HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Content-Type: application/json

{
    "@type": "Document",
    "title": "My Document"
}

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, like “description” here as well.

A special property during content creation is “UID”, as 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)

HTTP/1.1 201 Created
Content-Type: application/json
Location: http://localhost:55001/plone/folder/my-document

{
    "@components": {
        "actions": {
            "@id": "http://localhost:55001/plone/folder/my-document/@actions"
        },
        "breadcrumbs": {
            "@id": "http://localhost:55001/plone/folder/my-document/@breadcrumbs"
        },
        "contextnavigation": {
            "@id": "http://localhost:55001/plone/folder/my-document/@contextnavigation"
        },
        "navigation": {
            "@id": "http://localhost:55001/plone/folder/my-document/@navigation"
        },
        "types": {
            "@id": "http://localhost:55001/plone/folder/my-document/@types"
        },
        "workflow": {
            "@id": "http://localhost:55001/plone/folder/my-document/@workflow"
        }
    },
    "@id": "http://localhost:55001/plone/folder/my-document",
    "@type": "Document",
    "UID": "SomeUUID000000000000000000000005",
    "allow_discussion": false,
    "changeNote": "",
    "contributors": [],
    "created": "1995-07-31T13:45:00",
    "creators": [
        "admin"
    ],
    "description": "",
    "effective": null,
    "exclude_from_nav": false,
    "expires": null,
    "id": "my-document",
    "is_folderish": false,
    "language": "",
    "layout": "document_view",
    "lock": {
        "locked": false,
        "stealable": true
    },
    "modified": "1995-07-31T17:30:00",
    "next_item": {},
    "parent": {
        "@id": "http://localhost:55001/plone/folder",
        "@type": "Folder",
        "description": "This is a folder with two documents",
        "review_state": "private",
        "title": "My Folder"
    },
    "previous_item": {
        "@id": "http://localhost:55001/plone/folder/doc2",
        "@type": "Document",
        "description": "",
        "title": "A document within a folder"
    },
    "relatedItems": [],
    "review_state": "private",
    "rights": "",
    "subjects": [],
    "table_of_contents": null,
    "text": null,
    "title": "My Document",
    "version": "current",
    "versioning_enabled": true,
    "working_copy": null,
    "working_copy_of": null
}

Unsuccessful Response (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)

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  'message': 'Internal Server Error'
}

The response body can contain further information such as an error trace or a link to the documentation.

Possible POST Responses

Possible server reponses for a POST request are:

POST Implementation

A pseudo-code example of the POST implementation on the server:

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:

http

curl

httpie

python-requests

GET /plone/folder/my-document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

Successful Response (200 OK)

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@components": {
        "actions": {
            "@id": "http://localhost:55001/plone/folder/my-document/@actions"
        },
        "breadcrumbs": {
            "@id": "http://localhost:55001/plone/folder/my-document/@breadcrumbs"
        },
        "contextnavigation": {
            "@id": "http://localhost:55001/plone/folder/my-document/@contextnavigation"
        },
        "navigation": {
            "@id": "http://localhost:55001/plone/folder/my-document/@navigation"
        },
        "types": {
            "@id": "http://localhost:55001/plone/folder/my-document/@types"
        },
        "workflow": {
            "@id": "http://localhost:55001/plone/folder/my-document/@workflow"
        }
    },
    "@id": "http://localhost:55001/plone/folder/my-document",
    "@type": "Document",
    "UID": "SomeUUID000000000000000000000005",
    "allow_discussion": false,
    "changeNote": "",
    "contributors": [],
    "created": "1995-07-31T13:45:00",
    "creators": [
        "admin"
    ],
    "description": "",
    "effective": null,
    "exclude_from_nav": false,
    "expires": null,
    "id": "my-document",
    "is_folderish": false,
    "language": "",
    "layout": "document_view",
    "lock": {
        "locked": false,
        "stealable": true
    },
    "modified": "1995-07-31T17:30:00",
    "next_item": {},
    "parent": {
        "@id": "http://localhost:55001/plone/folder",
        "@type": "Folder",
        "description": "This is a folder with two documents",
        "review_state": "private",
        "title": "My Folder"
    },
    "previous_item": {
        "@id": "http://localhost:55001/plone/folder/doc2",
        "@type": "Document",
        "description": "",
        "title": "A document within a folder"
    },
    "relatedItems": [],
    "review_state": "private",
    "rights": "",
    "subjects": [],
    "table_of_contents": null,
    "text": null,
    "title": "My Document",
    "version": "current",
    "versioning_enabled": true,
    "working_copy": null,
    "working_copy_of": null
}

For folderish types, their childrens are automatically included in the response as items. To disable the inclusion, add the GET parameter include_items=false to the URL.

The following example additionaly retrieves the UID and Creator:

http

curl

httpie

python-requests

GET /plone/folder?metadata_fields=UID&metadata_fields=Creator HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
HTTP/1.1 200 OK
Content-Type: application/json

{
    "@components": {
        "actions": {
            "@id": "http://localhost:55001/plone/folder/@actions"
        },
        "breadcrumbs": {
            "@id": "http://localhost:55001/plone/folder/@breadcrumbs"
        },
        "contextnavigation": {
            "@id": "http://localhost:55001/plone/folder/@contextnavigation"
        },
        "navigation": {
            "@id": "http://localhost:55001/plone/folder/@navigation"
        },
        "types": {
            "@id": "http://localhost:55001/plone/folder/@types"
        },
        "workflow": {
            "@id": "http://localhost:55001/plone/folder/@workflow"
        }
    },
    "@id": "http://localhost:55001/plone/folder",
    "@type": "Folder",
    "UID": "SomeUUID000000000000000000000002",
    "allow_discussion": false,
    "contributors": [],
    "created": "1995-07-31T13:45:00",
    "creators": [
        "test_user_1_"
    ],
    "description": "This is a folder with two documents",
    "effective": null,
    "exclude_from_nav": false,
    "expires": null,
    "id": "folder",
    "is_folderish": true,
    "items": [
        {
            "@id": "http://localhost:55001/plone/folder/doc1",
            "@type": "Document",
            "Creator": "test_user_1_",
            "UID": "SomeUUID000000000000000000000003",
            "description": "",
            "review_state": "private",
            "title": "A document within a folder"
        },
        {
            "@id": "http://localhost:55001/plone/folder/doc2",
            "@type": "Document",
            "Creator": "test_user_1_",
            "UID": "SomeUUID000000000000000000000004",
            "description": "",
            "review_state": "private",
            "title": "A document within a folder"
        },
        {
            "@id": "http://localhost:55001/plone/folder/my-document",
            "@type": "Document",
            "Creator": "admin",
            "UID": "SomeUUID000000000000000000000005",
            "description": "",
            "review_state": "private",
            "title": "My Document"
        }
    ],
    "items_total": 3,
    "language": "",
    "layout": "listing_view",
    "lock": {},
    "modified": "1995-07-31T17:30:00",
    "nextPreviousEnabled": false,
    "next_item": {},
    "parent": {
        "@id": "http://localhost:55001/plone",
        "@type": "Plone Site",
        "description": "",
        "title": "Plone site"
    },
    "previous_item": {
        "@id": "http://localhost:55001/plone/front-page",
        "@type": "Document",
        "description": "Congratulations! You have successfully installed Plone.",
        "title": "Welcome to Plone"
    },
    "relatedItems": [],
    "review_state": "private",
    "rights": "",
    "subjects": [],
    "title": "My Folder",
    "version": "current",
    "working_copy": null,
    "working_copy_of": null
}

Note

Unsuccessful response (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:

try:
    order = getOrder()
    if order == None:
        # Not Found
        response.setStatus(404)
    else:
        # OK
        response.setStatus(200)
except:
    # Internal Server Error
    response.setStatus(500)

GET Responses

Possible server reponses for a GET request are:

Updating a Resource with PATCH

To update an existing resource we send a PATCH request to the server. PATCH allows to provide just a subset of the resource (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:

http

curl

httpie

python-requests

PATCH /plone/folder/my-document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Content-Type: application/json

{
    "title": "My New Document Title"
}

Successful Response (204 No Content)

HTTP/1.1 204 No Content

Successful Response (200 OK)

http

curl

httpie

python-requests

PATCH /plone/folder/my-document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Prefer: return=representation
Content-Type: application/json

{
    "title": "My New Document Title"
}
HTTP/1.1 200 OK
Content-Type: application/json

{
    "@components": {
        "actions": {
            "@id": "http://localhost:55001/plone/folder/my-document/@actions"
        },
        "breadcrumbs": {
            "@id": "http://localhost:55001/plone/folder/my-document/@breadcrumbs"
        },
        "contextnavigation": {
            "@id": "http://localhost:55001/plone/folder/my-document/@contextnavigation"
        },
        "navigation": {
            "@id": "http://localhost:55001/plone/folder/my-document/@navigation"
        },
        "types": {
            "@id": "http://localhost:55001/plone/folder/my-document/@types"
        },
        "workflow": {
            "@id": "http://localhost:55001/plone/folder/my-document/@workflow"
        }
    },
    "@id": "http://localhost:55001/plone/folder/my-document",
    "@type": "Document",
    "UID": "SomeUUID000000000000000000000005",
    "allow_discussion": false,
    "changeNote": "",
    "contributors": [],
    "created": "1995-07-31T13:45:00",
    "creators": [
        "admin"
    ],
    "description": "",
    "effective": null,
    "exclude_from_nav": false,
    "expires": null,
    "id": "my-document",
    "is_folderish": false,
    "language": "",
    "layout": "document_view",
    "lock": {
        "locked": false,
        "stealable": true
    },
    "modified": "1995-07-31T17:30:00",
    "next_item": {},
    "parent": {
        "@id": "http://localhost:55001/plone/folder",
        "@type": "Folder",
        "description": "This is a folder with two documents",
        "review_state": "private",
        "title": "My Folder"
    },
    "previous_item": {
        "@id": "http://localhost:55001/plone/folder/doc2",
        "@type": "Document",
        "description": "",
        "title": "A document within a folder"
    },
    "relatedItems": [],
    "review_state": "private",
    "rights": "",
    "subjects": [],
    "table_of_contents": null,
    "text": null,
    "title": "My New Document Title",
    "version": "current",
    "versioning_enabled": true,
    "working_copy": null,
    "working_copy_of": null
}

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.

Successful Update (204 No Content)

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:

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 reponses for a PUT request are:

POST vs. PUT

Difference between POST and PUT:

  • 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

Removing a Resource with DELETE

We can delete an existing resource by sending a DELETE request:

http

curl

httpie

python-requests

DELETE /plone/folder/my-document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
HTTP/1.1 204 No Content

DELETE Implementation

A pseudo-code example of the DELETE implementation on the server:

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:

Reordering sub resources

The resources contained within a resource can be reordered using the ordering key using 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 400 BadRequest with a message ‘Client/server ordering mismatch’ will be returned if the value is not in the same order as serverside.

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.

http

curl

httpie

python-requests

PATCH /plone/folder/my-document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Content-Type: application/json

{
    "ordering": {"obj_id": "item_3", "delta": "top", "subset_ids": ["item_1", "item_3", "item5"]}
}

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 ‘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.

http

curl

httpie

python-requests

PATCH /plone/folder HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Content-Type: application/json

{
    "sort": {"on": "modified", "order": "descending"}
}

Conventions

Naming Convention for REST API Resources/Endpoints

Nouns vs Verbs

Rule: Use nouns to represent resources.

Do:

/my-folder
/@registry
/@types

Don’t:

/createFolder
/deleteDocument
/updateEvent

Reason:

RESTful URI should refer to a resource that is a thing (noun) instead of referring to an action (verb) because nouns have properties as verbs do not. The REST architectural principle uses HTTP verbs to interact with resources.

Though, there is an exception to that rule, verbs can be used for specific actions or calculations, .e.g.:

/login
/logout
/move-to
/reset-password

Singluar vs Plural

Rule: Use plural resources.

Do:

/users
/users/21

Don’t:

/user
/user/21

Reason:

If you use singular for a collection like resource (e.g. “/user” to retrieve a list of all users) it feels wrong. Mixing singular and plural is confusing (e.g. user “/users” for retrieving users and “/user/21” to retrieve a single user).

Upper vs. Lowercase

Rule: Use lowercase letters in URIs.

Do:

http://example.com/my-folder/my-document

Don’t:

http://example.com/My-Folder/My-Document

Reason: RFC 3986 defines URIs as case-sensitive except for the scheme and host components. e.g.

Those two URIs are equivalent:

http://example.org/my-folder/my-document
HTTP://EXAMPLE.ORG/my-folder/my-document

While this one is not equivalent to the two URIs above:

http://example.org/My-Folder/my-document

To avoid confusion we always use lowercase letters in URIs.

Naming Convention for attribute names in URIs

Rule: Use hyphens (spinal case) to improve readability of URIs.

Do:

/users/noam/reset-password

Don’t:

/users/noam/resetPassword
/users/noam/ResetPassword
/users/noam/reset_password

Reason:

Discussion:

Naming Convention for attribute names in response body

Rule: Use snake_case to reflect Python best practices.

Do:

{
  translation_of: ...
}

Don’t:

{
  translationOf: ...,
  TranslationOf: ...,
}

Reason:

We map over Python attributes 1:1 no matter if they are snake case (modern Python/Plone, Dexterity) of lowerCamelCase (Zope 2, Archetypes).

Versioning

Versioning APIs does make a lot of sense for public API services. Especially if an API provider needs to ship different versions of the API at the same time. Though, Plone already has a way to version packages and it currently does not make sense for us to expose that information via the API. We will always just ship one version of the API at a time and we are usually in full control over the backend and the frontend.

To discover the API interactively, using is recommended.

postman-retain-headers

This option makes sure, once a is configured, it will be reused during following , if these are initiated by clicking on links resulting from the initial . This way navigating the structure using the API becomes a snap.

Choose the suitable to be used for your request. This can be selected using the HTTP Verb -> GET drop-down menu.

Enter the of the object that should be the target of a request into the URL field right to the :

postman-request

The for the authentication related to the right user

The to initiate the right behaviour by the API related to this .

To set the , there is a reserved tab, that is responsible to generate the final based on the and username + password.

in the drop-down menu Basic Auth -> the term as the authentication method

After providing these parameters you can create the resulting and insert it into the prepared request by clicking on Preview Request.

postman-basic-auth

Under the Headers tab you now need to insert in the application/json` header as well:

postman-headers

The of the server is now displayed below the . You can easily follow the links on the @id attributes by clicking on them. For every link has prepared another request sharing the same headers that can be send again by licking on the Send button.

postman-response

If a resource has been created, the server responds with the status code. The ‘Location’ header contains the URL of the newly created resource and the resource representation in the payload:

If the resource could not be created, for instance because the title was missing in the request, the server responds with :

If the server can not properly process a request, it responds with :

(Resource has been created successfully)

(malformed request to the service)

(server fault, can not recover internally)

If a resource has been retrieved successfully, the server responds with :

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 .

For folderish types, collections or search results, the results will be batched if the size of the resultset exceeds the batch size. See for more details on how to work with batched results.

If a resource could not be found, the server will respond with :

You can find implementation details in the

A successful response to a PATCH request will be indicated by a response by default:

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 :

See for full specs the

When the PUT request is accepted and processed by the service, the consumer will receive a response ( would be a valid alternative).

When a resource has been updated successfully, the server sends a response:

This follows .

A successful response will be indicated by a response:

(if the resource does not exist)

(if deleting the resource is not allowed)

Spinal case is better to read and safer to use than camelCase (URLs are case sensitive (RFC3986)). Plone uses spinal case for URL creation (title “My page” becomes “my-page”) and mixed naming conventions in URLs would be confusing (e.g. “”). Google recommends spinal-case in URLs for better SEO ().

Postman
Explore the API using Postman
REST
More on Content Negotiation
Representation of all standard Plone contenttypes
Postman
HTTP-Header
requests
request
HTTP Verb
Postman
Object URL
HTTP Verb
Authorization Header
Accept Header
Request
Authorization Header
Header
authentication method
Basic Auth
Authorization Header
Accept Header
Response
Request
Postman
201 Created
400 Bad Request
500 Internal Server Error
201 Created
400 Bad Request
500 Internal Server Error
200 OK
Retrieving additional metadata
Batching
404 Not Found
plone.restapi.services.content.add.FolderPost class
200 OK
404 Not Found
500 Internal Server Error
204 No Content
200 OK
RFC 5789: PATCH Method for HTTP
204 No Content
200 OK
204 No Content
200 OK
404 Not Found
409 Conflict
500 Internal Server Error
RFC 7231: HTTP 1.1: PUT Method
204 No Content
204 No Content
404 Not Found
405 Method Not Allowed
500 Internal Server Error
/my-folder/@send_url_to_user
https://support.google.com/webmasters/answer/76329
https://github.com/plone/plone.restapi/issues/194