Browse Source

API v2 docs (#2940)

Karl Hobley 8 years ago
parent
commit
ebe597610e

+ 29 - 0
docs/advanced_topics/api/index.rst

@@ -0,0 +1,29 @@
+===========
+Wagtail API
+===========
+
+The API module provides a public-facing, JSON-formatted API to allow retrieving
+content as raw field data. This is useful for cases like serving content to
+non-web clients (such as a mobile phone app) or pulling content out of Wagtail
+for use in another site.
+
+There are currently two versions of the API available: v1 and v2. Both versions
+are "stable" so it is recommended to use v2. V1 is only provided for backwards
+compatibility and will be removed from Wagtail soon.
+
+See `RFC 8: Wagtail API <https://github.com/torchbox/wagtail-rfcs/blob/master/accepted/008-wagtail-api.rst#id4>`_
+for full details on our stabilisation policy.
+
+Version 2 (recommended)
+=======================
+
+.. toctree::
+    :maxdepth: 2
+
+    v2/configuration
+    v2/usage
+
+Version 1
+=========
+
+See :doc:`/reference/contrib/api/index`

+ 166 - 0
docs/advanced_topics/api/v2/configuration.rst

@@ -0,0 +1,166 @@
+==================================
+Wagtail API v2 Configuration Guide
+==================================
+
+This section of the docs will show you how to set up a public API for your
+Wagtail site.
+
+Even though the API is built on Django REST Framework, you do not need to
+install this manually as it is already a dependency of Wagtail.
+
+Basic configuration
+===================
+
+Enable the app
+--------------
+
+Firstly, you need to enable Wagtail's API app so Django can see it.
+Add ``wagtail.api.v2`` to ``INSTALLED_APPS`` in your Django project settings:
+
+.. code-block:: python
+
+    # settings.py
+
+    INSTALLED_APPS = [
+        ...
+
+        'wagtail.api.v2',
+
+        ...
+    ]
+
+Optionally, you may also want to add ``restframework`` to ``INSTALLED_APPS``.
+This would make the API browsable when viewed from a web browser but is not
+required for basic JSON-formatted output.
+
+Configure endpoints
+-------------------
+
+Next, it's time to configure which content will be exposed on the API. Each
+content type (such as pages, images and documents) has its own endpoint.
+Endpoints are combined by a router, which provides the url configuration you
+can hook into the rest of your project.
+
+Wagtail provides three endpoint classes you can use:
+
+ - Pages :class:`wagtail.api.v2.endpoints.PagesAPIEndpoint`
+ - Images :class:`wagtail.wagtailimages.api.v2.endpoints.ImagesAPIEndpoint`
+ - Documents :class:`wagtail.wagtaildocs.api.v2.endpoints.DocumentsAPIEndpoint`
+
+You can subclass any of these endpoint classes to customise their functionality.
+Additionally, there is a base endpoint class you can use for adding different
+content types to the API: :class:`wagtail.api.v2.endpoints.BaseAPIEndpoint`
+
+For this example, we will create an API that includes all three builtin content
+types in their default configuration:
+
+.. code-block:: python
+
+    # api.py
+
+    from wagtail.api.v2.endpoints import PagesAPIEndpoint
+    from wagtail.api.v2.router import WagtailAPIRouter
+    from wagtail.wagtailimages.api.v2.endpoints import ImagesAPIEndpoint
+    from wagtail.wagtaildocs.api.v2.endpoints import DocumentsAPIEndpoint
+
+    # Create the router. "wagtailapi" is the URL namespace
+    api_router = WagtailAPIRouter('wagtailapi')
+
+    # Add the three endpoints using the "register_endpoint" method.
+    # The first parameter is the name of the endpoint (eg. pages, images). This
+    # is used in the URL of the endpoint
+    # The second parameter is the endpoint class that handles the requests
+    api_router.register_endpoint('pages', PagesAPIEndpoint)
+    api_router.register_endpoint('images', ImagesAPIEndpoint)
+    api_router.register_endpoint('documents', DocumentsAPIEndpoint)
+
+Next, register the URLs so Django can route requests into the API:
+
+.. code-block:: python
+
+    # urls.py
+
+    from .api import api_router
+
+    urlpatterns = [
+        ...
+
+        url(r'^api/v2/', api_router.urls),
+
+        ...
+    ]
+
+With this configuration, pages will be available at ``/api/v2/pages/``, images
+at ``/api/v2/images/`` and documents at ``/api/v2/documents/``
+
+.. _apiv2_page_fields_configuration:
+
+Adding custom page fields
+-------------------------
+
+It's likely that you would need to export some custom fields over the API. This
+can be done by adding a list of fields to be exported into the ``api_fields``
+attribute for each page model.
+
+For example:
+
+.. code-block:: python
+
+    # blog/models.py
+
+    class BlogPageAuthor(Orderable):
+        page = models.ForeignKey('blog.BlogPage', related_name='authors')
+        name = models.CharField(max_length=255)
+
+        api_fields = ['name']
+
+
+    class BlogPage(Page):
+        published_date = models.DateTimeField()
+        body = RichTextField()
+        feed_image = models.ForeignKey('wagtailimages.Image', ...)
+        private_field = models.CharField(max_length=255)
+
+        # Export fields over the API
+        api_fields = [
+            'published_date',
+            'body',
+            'feed_image',
+            'authors',  # This will nest the relevant BlogPageAuthor objects in the API response
+        ]
+
+This will make ``published_date``, ``body``, ``feed_image`` and a list of
+``authors`` with the ``name`` field available in the API. But to access these
+fields, you must select the ``blog.BlogPage`` type using the ``?type``
+:ref:`parameter in the API itself <apiv2_custom_page_fields>`.
+
+Additional settings
+===================
+
+``WAGTAILAPI_BASE_URL``
+-----------------------
+
+(required when using frontend cache invalidation)
+
+This is used in two places, when generating absolute URLs to document files and
+invalidating the cache.
+
+Generating URLs to documents will fall back the the current request's hostname
+if this is not set. Cache invalidation cannot do this, however, so this setting
+must be set when using this module alongside the ``wagtailfrontendcache`` module.
+
+``WAGTAILAPI_SEARCH_ENABLED``
+-----------------------------
+
+(default: True)
+
+Setting this to false will disable full text search. This applies to all
+endpoints.
+
+``WAGTAILAPI_LIMIT_MAX``
+------------------------
+
+(default: 20)
+
+This allows you to change the maximum number of results a user can request at a
+time. This applies to all endpoints.

+ 542 - 0
docs/advanced_topics/api/v2/usage.rst

@@ -0,0 +1,542 @@
+==========================
+Wagtail API v2 Usage Guide
+==========================
+
+.. note::
+
+   This is the usage guide for version 2 of the API. For version 1, see
+   :doc:`/reference/contrib/api/usage`
+
+The Wagtail API module exposes a public, read only, JSON-formatted API which
+can be used by external clients (such as a mobile app) or the site's frontend.
+
+This document is intended for developers using the API exposed by Wagtail. For
+documentation on how to enable the API module in your Wagtail site, see
+:doc:`/advanced_topics/api/v2/configuration`
+
+.. contents::
+
+Fetching content
+================
+
+To fetch content over the API, perform a ``GET`` request against one of the
+following endpoints:
+
+ - Pages ``/api/v2/pages/``
+ - Images ``/api/v2/images/``
+ - Documents ``/api/v2/documents/``
+
+.. note::
+
+    The available endpoints and their URLs may vary from site to site, depending
+    on how the API has been configured.
+
+Example response
+----------------
+
+Each response contains the list of items (``items``) and the total count
+(``meta.total_count``). The total count is irrespective of pagination.
+
+.. code-block:: text
+
+    GET /api/v2/endpoint_name/
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": "total number of results"
+        },
+        "items": [
+            {
+                "id": 1,
+                "meta": {
+                    "type": "app_name.ModelName",
+                    "detail_url": "http://api.example.com/api/v2/endpoint_name/1/"
+                },
+                "field": "value"
+            },
+            {
+                "id": 2,
+                "meta": {
+                    "type": "app_name.ModelName",
+                    "detail_url": "http://api.example.com/api/v2/endpoint_name/2/"
+                },
+                "field": "different value"
+            }
+        ]
+    }
+
+.. _apiv2_custom_page_fields:
+
+Custom page fields in the API
+-----------------------------
+
+Wagtail sites contain many page types, each with their own set of fields. The
+``pages`` endpoint will only expose the common fields by default (such as
+``title`` and ``slug``).
+
+To access custom page fields with the API, select the page type with the
+``?type`` parameter. This will filter the results to only include pages of that
+type but will also make all the exported custom fields for that type available
+in the API.
+
+For example, to access the ``published_date``, ``body`` and ``authors`` fields
+on the ``blog.BlogPage`` model in the :ref:`configuration docs <apiv2_page_fields_configuration>`:
+
+.. code-block:: text
+
+    GET /api/v2/pages/?type=blog.BlogPage&fields=published_date,body,authors(name)
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 10
+        },
+        "items": [
+            {
+                "id": 1,
+                "meta": {
+                    "type": "blog.BlogPage",
+                    "detail_url": "http://api.example.com/api/v2/pages/1/",
+                    "html_url": "http://www.example.com/blog/my-blog-post/",
+                    "slug": "my-blog-post",
+                    "first_published_at": "2016-08-30T16:52:00Z"
+                },
+                "title": "Test blog post",
+                "published_date": "2016-08-30",
+                "authors": [
+                    {
+                        "id": 1,
+                        "meta": {
+                            "type": "blog.BlogPageAuthor",
+                        },
+                        "name": "Karl Hobley"
+                    }
+                ]
+            },
+
+            ...
+        ]
+    }
+
+.. note::
+
+    Only fields that have been explicitly exported by the developer may be used
+    in the API. This is done by adding a ``api_fields`` attribute to the page
+    model. You can read about configuration :ref:`here <apiv2_page_fields_configuration>`.
+
+This doesn't apply to images/documents as there is only one model exposed in
+those endpoints. But for projects that have customised image/document models,
+the ``api_fields`` attribute can be used to export any custom fields into the
+API.
+
+Pagination
+----------
+
+The number of items in the response can be changed by using the ``?limit``
+parameter (default: 20) and the number of items to skip can be changed by using
+the ``?offset`` parameter.
+
+For example:
+
+.. code-block:: text
+
+    GET /api/v2/pages/?offset=20&limit=20
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 50
+        },
+        "items": [
+            pages 20 - 40 will be listed here.
+        ]
+    }
+
+.. note::
+
+    There may be a maximum value for the ``?limit`` parameter. This can be
+    modified in your project settings by setting ``WAGTAILAPI_LIMIT_MAX`` to
+    either a number (the new maximum value) or ``None`` (which disables maximum
+    value check).
+
+Ordering
+--------
+
+The results can be ordered by any field by setting the ``?order`` parameter to
+the name of the field to order by.
+
+.. code-block:: text
+
+    GET /api/v2/pages/?order=title
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 50
+        },
+        "items": [
+            pages will be listed here in ascending title order (a-z)
+        ]
+    }
+
+The results will be ordered in ascending order by default. This can be changed
+to descending order by prefixing the field name with a ``-`` sign.
+
+.. code-block:: text
+
+    GET /api/v2/pages/?order=-title
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 50
+        },
+        "items": [
+            pages will be listed here in descending title order (z-a)
+        ]
+    }
+
+.. note::
+
+    Ordering is case-sensitive so lowercase letters are always ordered after
+    uppercase letters when in ascending order.
+
+Random ordering
+^^^^^^^^^^^^^^^
+
+Passing ``random`` into the ``?order`` parameter will make results return in a
+random order. If there is no caching, each request will return results in a
+different order.
+
+.. code-block:: text
+
+    GET /api/v2/pages/?order=random
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 50
+        },
+        "items": [
+            pages will be listed here in random order
+        ]
+    }
+
+.. note::
+
+    It's not possible to use ``?offset`` while ordering randomly because
+    consistent random ordering cannot be guarenteed over multiple requests
+    (so requests for subsequent pages may return results that also appeared in
+    previous pages).
+
+Filtering
+---------
+
+Any field may be used in an exact match filter. Use the filter name as the
+parameter and the value to match against.
+
+For example, to find a page with the slug "about":
+
+.. code-block:: text
+
+    GET /api/v2/pages/?slug=about
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 1
+        },
+        "items": [
+            {
+                "id": 10,
+                "meta": {
+                    "type": "standard.StandardPage",
+                    "detail_url": "http://api.example.com/api/v2/pages/10/",
+                    "html_url": "http://www.example.com/about/",
+                    "slug": "about",
+                    "first_published_at": "2016-08-30T16:52:00Z"
+                },
+                "title": "About"
+            },
+        ]
+    }
+
+Filtering by tree position (pages only)
+---------------------------------------
+
+Pages can additionally be filtered by their position of the tree. For this,
+there are two parameters you can use: ``?child_of`` and ``?descendant_of``.
+
+The ``?child_of`` filter takes the id of a page and filters the list of results
+to contain only direct children of that page.
+
+For example, this can be useful for constructing the main menu, by passing the
+id of the homepage to the filter:
+
+.. code-block:: text
+
+    GET /api/v2/pages/?child_of=2&show_in_menus=true
+
+    HTTP 200 OK
+    Content-Type: application/json
+
+    {
+        "meta": {
+            "total_count": 5
+        },
+        "items": [
+            {
+                "id": 3,
+                "meta": {
+                    "type": "blog.BlogIndexPage",
+                    "detail_url": "http://api.example.com/api/v2/pages/3/",
+                    "html_url": "http://www.example.com/blog/",
+                    "slug": "blog",
+                    "first_published_at": "2016-09-21T13:54:00Z"
+                },
+                "title": "About"
+            },
+            {
+                "id": 10,
+                "meta": {
+                    "type": "standard.StandardPage",
+                    "detail_url": "http://api.example.com/api/v2/pages/10/",
+                    "html_url": "http://www.example.com/about/",
+                    "slug": "about",
+                    "first_published_at": "2016-08-30T16:52:00Z"
+                },
+                "title": "About"
+            },
+
+            ...
+        ]
+    }
+
+The ``?descendant_of`` filter also takes the id of a page but includes all
+descendants (children of children) instead of just directly children.
+
+Search
+------
+
+Passing a query to the ``?search`` parameter will perform a full-text search on
+the results.
+
+The query is split into "terms" (by word boundary), then each term is normalised
+(lowercased and unaccented).
+
+For example: ``?search=James+Joyce``
+
+Search operator
+^^^^^^^^^^^^^^^
+
+The ``search_operator`` specifies how multiple terms in the query should be
+handled. There are two possible values:
+
+ - ``and`` - All terms in the search query (excluding stop words) must exist in
+   each result
+ - ``or`` - At least one term in the search query must exist in each result
+
+The ``or`` operator is generally better than ``and`` as it allows the user to be
+inexact with their query and the ranking algorithm will make sure that
+irrelevant results are not returned at the top of the page.
+
+The default search operator depends on whether the search engine being used by
+the site supports ranking. If it does (Elasticsearch), the operator will default
+to ``or``. Otherwise (database), it will default to ``and``.
+
+For the same reason, it's also recommended to use the ``and`` operator when
+using ``?search`` in conjunction with ``?order`` (as this disables ranking).
+
+For example: ``?search=James+Joyce&order=-first_published_at&search_operator=and``
+
+Fields
+------
+
+By default, only a subset of the available fields are returned in the response.
+The ``?fields`` parameter can be used to both add additional fields to the
+response and remove default fields that you know you won't need.
+
+Additional fields
+^^^^^^^^^^^^^^^^^
+
+Additional fields can be added to the response by setting ``?fields`` to a
+comma-separated list of field names you want to add.
+
+For example, ``?fields=body,feed_image`` will add the ``body`` and ``feed_image``
+fields to the response.
+
+This can also be used across relationships. For example,
+``?fields=body,feed_image(width,height)`` will nest the ``width`` and ``height``
+of the image in the response.
+
+All fields
+^^^^^^^^^^
+
+Setting ``?fields`` to an asterisk (``*``) will add all available fields to the
+response. This is useful for discovering what fields have been exported.
+
+For example: ``?fields=*``
+
+Removing fields
+^^^^^^^^^^^^^^^
+
+Fields you know that you do not need can be removed by prefixing the name with a
+``-`` and adding it to ``?fields``.
+
+For example, ``?fields=-title,body`` will remove ``title`` and add ``body``.
+
+This can also be used with the asterisk. For example, ``?fields=*,-body``
+adds all fields except for ``body``.
+
+Removing all default fields
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To specify exactly the fields you need, you can set the first item in fields to
+an underscore (``_``) which removes all default fields.
+
+For example, ``?fields=_,title`` will only return the title field.
+
+Detail views
+------------
+
+You can retrieve a single object from the API by appending its id to the end of
+the URL. For example:
+
+- Pages ``/api/v2/pages/1/``
+- Images ``/api/v2/images/1/``
+- Documents ``/api/v2/documents/1/``
+
+All exported fields will be returned in the response by default. You can use the
+``?fields`` parameter to customise which fields are shown.
+
+For example: ``/api/v2/pages/1/?fields_,title,body`` will return just the
+``title`` and ``body`` of the page with the id of 1.
+
+Default endpoint fields
+=======================
+
+Common fields
+-------------
+
+These fields are returned by every endpoint.
+
+.. glossary::
+
+    ``id``  (number)
+
+        The unique ID of the object
+
+        .. note::
+
+            Except for page types, every other content type has its own id space
+            so you must combine this with the ``type`` field in order to get a
+            unique identifier for an object.
+
+    ``type`` (string)
+
+        The type of the object in ``app_label.ModelName`` format
+
+    ``detail_url``  (string)
+
+        The URL of the detail view for the object
+
+Pages
+-----
+
+.. glossary::
+
+    ``title``  (string)
+    ``meta.slug``  (string)
+    ``meta.show_in_menus``  (boolean)
+    ``meta.seo_title``  (string)
+    ``meta.search_description``  (string)
+    ``meta.first_published_at``  (date/time)
+
+        These values are taken from their corresponding fields on the page
+
+    ``meta.html_url``  (string)
+
+        If the site has an HTML frontend that's generated by Wagtail, this
+        field will be set to the URL of this page
+
+    ``meta.parent``
+
+        Nests some information about the parent page (only available on detail
+        views)
+
+Images
+------
+
+.. glossary::
+
+    ``title``  (string)
+
+        The value of the image's title field. Within Wagtail, this is used in
+        the image's ``alt`` HTML attribute.
+
+    ``width``  (number)
+    ``height``  (number)
+
+        The size of the original image file
+
+    ``meta.tags``  (list of strings)
+
+        A list of tags associated with the image
+
+Documents
+---------
+
+.. glossary::
+
+    ``title``  (string)
+
+        The value of the document's title field
+
+    ``meta.tags``  (list of strings)
+
+        A list of tags associated with the document
+
+    ``meta.download_url``  (string)
+
+        A URL to the document file
+
+Changes since v1
+================
+
+Breaking changes
+----------------
+
+ - The results list in listing responses has been renamed to ``items`` (was
+   previously either ``pages``, ``images`` or ``documents``)
+
+Major features
+--------------
+
+ - The ``fields`` parameter has been improved to allow removing fields, adding
+   all fields and customising nested fields
+
+Minor features
+--------------
+
+ - ``html_url``, ``slug``, ``first_publised_at``, ``expires_at`` and
+   ``show_in_menus`` fields have been added to the pages endpoint
+ - ``download_url`` field has been added to the documents endpoint
+ - Multiple page types can be specified in ``type`` parameter on pages endpoint
+ - ``true`` and ``false`` may now be used when filtering boolean fields
+ - ``order`` can now be used in conjunction with ``search``
+ - ``search_operator`` parameter was added

+ 1 - 0
docs/advanced_topics/index.rst

@@ -15,3 +15,4 @@ Advanced topics
     third_party_tutorials
     jinja2
     testing
+    api/index