diff --git a/docs/source/developers/basic-extension.rst b/docs/source/developers/basic-extension.rst
index b67b926d06..5affbeb1d4 100644
--- a/docs/source/developers/basic-extension.rst
+++ b/docs/source/developers/basic-extension.rst
@@ -1,2 +1,298 @@
+=========================
+Jupyter Server Extensions
+=========================
+
+A Jupyter Server extension is typically a module or package that extends to Server’s REST API/endpoints—i.e. adds extra request handlers to Server’s Tornado Web Application.
+
Authoring a basic server extension
-==================================
\ No newline at end of file
+==================================
+
+The simplest way to write a Jupyter Server extension is to write an extension module with a ``_load_jupyter_server_extension`` function. This function should take a single argument, an instance of the ``ServerApp``.
+
+
+.. code-block:: python
+
+ def _load_jupyter_server_extension(serverapp):
+ """
+ This function is called when the extension is loaded.
+ """
+ pass
+
+
+Adding extension endpoints
+--------------------------
+
+The easiest way to add endpoints and handle incoming requests is to subclass the ``JupyterHandler`` (which itself is a subclass of Tornado's ``RequestHandler``).
+
+.. code-block:: python
+
+ from jupyter_server.base.handlers import JupyterHandler
+
+ class MyExtensionHandler(JupyterHandler):
+
+ def get(self):
+ ...
+
+ def post(self):
+ ...
+
+
+Then add this handler to Jupyter Server's Web Application through the ``_load_jupyter_server_extension`` function.
+
+.. code-block:: python
+
+ def _load_jupyter_server_extension(serverapp):
+ """
+ This function is called when the extension is loaded.
+ """
+ handlers = [
+ ('/myextension/hello', MyExtensionHandler)
+ ]
+ serverapp.web_app.add_handlers('.*$', handlers)
+
+
+
+Making an extension discoverable
+--------------------------------
+
+To make this extension discoverable to Jupyter Server, there are two steps. First, the extension module must define a ``_jupyter_server_extension_paths()`` function that returns some metadata about the extension entry-points in the module. This informs Jupyter Server what type of extension is being loaded and where to find the ``_load_jupyter_server_extension``.
+
+.. code-block:: python
+
+ def _jupyter_server_extension_paths():
+ """
+ Returns a list of dictionaries with metadata describing
+ where to find the `_load_jupyter_server_extension` function.
+ """
+ return [
+ {
+ "module": "my_extension"
+ }
+ ]
+
+Second, the extension must be listed in the user’s ``jpserver_extensions`` config trait. This can be manually added by users in their ``jupyter_server_config.py`` file:
+
+.. code-block:: python
+
+ c.ServerApp.jpserver_extensions = {
+ "my_extension": True
+ }
+
+Alternatively, an extension can automatically enable itself by creating the following JSON in the jupyter_server_config.d directory on installation. See XX for more details.
+
+.. code-block:: python
+
+ {
+ "ServerApp": {
+ "jpserver_extensions": {
+ "my_extension": true
+ }
+ }
+ }
+
+
+Authoring a configurable extension application
+==============================================
+
+Some extensions are full-fledged client applications that sit on top of the Jupyter Server. For example, `JupyterLab `_ is a server extension. It can be launched from the command line, configured by CLI or config files, and serves+loads static assets behind the server (i.e. html templates, Javascript, etc.)
+
+Jupyter Server offers a convenient base class, ``ExtensionsApp``, that handles most of the boilerplate code for building such extensions.
+
+Anatomy of an ``ExtensionApp``
+------------------------------
+
+An ExtensionApp:
+
+ - has traits.
+ - is configurable (from file or CLI)
+ - has a name (see the ``extension_name`` trait).
+ - has an entrypoint, ``jupyter ``.
+ - can server static content from the ``/static//`` endpoint.
+ - can add new endpoints to the Jupyter Server.
+
+The basic structure of an ExtensionApp is shown below:
+
+.. code-block:: python
+
+ from jupyter_server.extension.application import ExtensionApp
+
+
+ class MyExtensionApp(ExtensionApp):
+
+ # -------------- Required traits --------------
+ extension_name = "myextension"
+ extension_url = "/myextension"
+ load_other_extensions = True
+
+ # --- ExtensionApp traits you can configure ---
+ static_paths = [...]
+ template_paths = [...]
+ settings = {...}
+ handlers = [...]
+
+ # ----------- add custom traits below ---------
+ ...
+
+ def initialize_settings(self):
+ ...
+ # Update the self.settings trait to pass extra
+ # settings to the underlying Tornado Web Application.
+ self.settings.update({'':...})
+
+ def initialize_handlers(self):
+ ...
+ # Extend the self.handlers trait
+ self.handlers.extend(...)
+
+ def initialize_templates(self):
+ ...
+ # Change the jinja templating environment
+
+
+The ``ExtensionApp`` uses the following methods and properties to connect your extension to the Jupyter server. You do no need to define a ``_load_jupyter_server_extension`` function for these apps. Instead, overwrite the pieces below to add your custom settings, handlers and templates:
+
+Methods
+
+* ``initialize_setting()``: adds custom settings to the Tornado Web Application.
+* ``initialize_handlers()``: appends handlers to the Tornado Web Application.
+* ``initialize_templates()``: initialize the templating engine (e.g. jinja2) for your frontend.
+
+Properties
+
+* ``extension_name``: the name of the extension
+* ``extension_url``: the default url for this extension—i.e. the landing page for this extension when launched from the CLI.
+* ``load_other_extensions``: a boolean enabling/disabling other extensions when launching this extension directly.
+
+``ExtensionApp`` request handlers
+---------------------------------
+
+``ExtensionApp`` Request Handlers have a few extra properties.
+
+* ``config``: the ExtensionApp's config object.
+* ``server_config``: the ServerApp's config object.
+* ``extension_name``: the name of the extension to which this handler is linked.
+* ``static_url()``: a method that returns the url to static files (prefixed with ``/static/``).
+
+Jupyter Server provides a convenient mixin class for adding these properties to any ``JupyterHandler``. For example, the basic server extension handler in the section above becomes:
+
+.. code-block:: python
+
+ from jupyter_server.base.handlers import JupyterHandler
+ from jupyter_server.extension.handler import ExtensionHandlerMixin
+
+
+ class MyExtensionHandler(ExtensionHandlerMixin, JupyterHandler):
+
+ def get(self):
+ ...
+
+ def post(self):
+ ...
+
+
+Jinja templating from frontend extensions
+-----------------------------------------
+
+Many Jupyter frontend applications use Jinja for basic HTML templating. Since this is common enough, Jupyter Server provides some extra mixin that integrate Jinja with Jupyter server extensions.
+
+Use ``ExtensionAppJinjaMixin`` to automatically add a Jinja templating environment to an ``ExtensionApp``. This adds a ``_jinja2_env`` setting to Tornado Web Server's settings that be be used by request handlers.
+
+.. code-block:: python
+
+
+ from jupyter_server.extension.application import ExtensionApp, ExtensionAppJinjaMixin
+
+
+ class MyExtensionApp(ExtensionAppJinjaMixin, ExtensionApp):
+ ...
+
+
+Pair the example above with ``ExtensionHandlers`` that also inherit the ``ExtensionHandlerJinjaMixin`` mixin. This will automatically load HTML templates from the Jinja templating environment created by the ``ExtensionApp``.
+
+
+.. code-block:: python
+
+
+ from jupyter_server.base.handlers import JupyterHandler
+ from jupyter_server.extension.handler import (
+ ExtensionHandlerMixin,
+ ExtensionHandlerJinjaMixin
+ )
+
+ class MyExtensionHandler(
+ ExtensionHandlerMixin,
+ ExtensionHandlerJinjaMixin,
+ JupyterHandler
+ ):
+
+ def get(self):
+ ...
+
+ def post(self):
+ ...
+
+
+.. note:: The mixin classes in this example must come before the base classes, ``ExtensionApp`` and ``ExtensionHandler``.
+
+
+Making an ``ExtensionApp`` discoverable
+---------------------------------------
+
+To make an ``ExtensionApp`` discoverable by Jupyter Server, add the ``app`` key+value pair to the ``_jupyter_server_extension_paths()`` function example above:
+
+.. code-block:: python
+
+ from myextension import MyExtensionApp
+
+
+ def _jupyter_server_extension_paths():
+ """
+ Returns a list of dictionaries with metadata describing
+ where to find the `_load_jupyter_server_extension` function.
+ """
+ return [
+ {
+ "module": "myextension",
+ "app": MyExtensionApp
+ }
+ ]
+
+
+Launching an ``ExtensionApp``
+-----------------------------
+
+To launch the application, simply call the ``ExtensionApp``'s ``launch_instance`` method.
+
+.. code-block:: python
+
+ launch_instance = MyFrontend.launch_instance
+ launch_instance()
+
+
+To make your extension executable from anywhere on your system, point an entry-point at the ``launch_instance`` method in the extension's ``setup.py``:
+
+.. code-block:: python
+
+ from setuptools import setup
+
+
+ setup(
+ name='myfrontend',
+ ...
+ entry_points={
+ 'console_scripts': [
+ 'jupyter-myextension = myextension:launch_instance'
+ ]
+ }
+ )
+
+Distributing a server extension
+===============================
+
+
+
+Example Server Extension
+========================
+
+You can check some simple example on the `GitHub jupyter_server repository
+`_.
diff --git a/docs/source/developers/dependency.rst b/docs/source/developers/dependency.rst
index d69aa28d7d..cb33e3330e 100644
--- a/docs/source/developers/dependency.rst
+++ b/docs/source/developers/dependency.rst
@@ -18,4 +18,4 @@ To pin your jupyter_server install to a specific version:
.. code-block:: console
- > pip install jupyter_server==1.0
\ No newline at end of file
+ > pip install jupyter_server==1.0
diff --git a/docs/source/developers/index.rst b/docs/source/developers/index.rst
index 1266911dff..30be2e9f09 100644
--- a/docs/source/developers/index.rst
+++ b/docs/source/developers/index.rst
@@ -10,6 +10,5 @@ These pages target people writing Jupyter Web applications and server extensions
dependency
basic-extension
- configurable-extension
rest-api
../other/changelog
\ No newline at end of file
diff --git a/docs/source/extending/contents.rst b/docs/source/extending/contents.rst
deleted file mode 100644
index cd0f7f7aaf..0000000000
--- a/docs/source/extending/contents.rst
+++ /dev/null
@@ -1,269 +0,0 @@
-.. _contents_api:
-
-Contents API
-============
-
-.. currentmodule:: jupyter_server.services.contents
-
-The Jupyter Notebook web application provides a graphical interface for
-creating, opening, renaming, and deleting files in a virtual filesystem.
-
-The :class:`~manager.ContentsManager` class defines an abstract
-API for translating these interactions into operations on a particular storage
-medium. The default implementation,
-:class:`~filemanager.FileContentsManager`, uses the local
-filesystem of the server for storage and straightforwardly serializes notebooks
-into JSON. Users can override these behaviors by supplying custom subclasses
-of ContentsManager.
-
-This section describes the interface implemented by ContentsManager subclasses.
-We refer to this interface as the **Contents API**.
-
-Data Model
-----------
-
-.. currentmodule:: jupyter_server.services.contents.manager
-
-Filesystem Entities
-~~~~~~~~~~~~~~~~~~~
-.. _notebook models:
-
-ContentsManager methods represent virtual filesystem entities as dictionaries,
-which we refer to as **models**.
-
-Models may contain the following entries:
-
-+--------------------+-----------+------------------------------+
-| Key | Type |Info |
-+====================+===========+==============================+
-|**name** |unicode |Basename of the entity. |
-+--------------------+-----------+------------------------------+
-|**path** |unicode |Full |
-| | |(:ref:`API-style`) |
-| | |path to the entity. |
-+--------------------+-----------+------------------------------+
-|**type** |unicode |The entity type. One of |
-| | |``"notebook"``, ``"file"`` or |
-| | |``"directory"``. |
-+--------------------+-----------+------------------------------+
-|**created** |datetime |Creation date of the entity. |
-+--------------------+-----------+------------------------------+
-|**last_modified** |datetime |Last modified date of the |
-| | |entity. |
-+--------------------+-----------+------------------------------+
-|**content** |variable |The "content" of the entity. |
-| | |(:ref:`See |
-| | |Below`) |
-+--------------------+-----------+------------------------------+
-|**mimetype** |unicode or |The mimetype of ``content``, |
-| |``None`` |if any. (:ref:`See |
-| | |Below`) |
-+--------------------+-----------+------------------------------+
-|**format** |unicode or |The format of ``content``, |
-| |``None`` |if any. (:ref:`See |
-| | |Below`) |
-+--------------------+-----------+------------------------------+
-
-.. _modelcontent:
-
-Certain model fields vary in structure depending on the ``type`` field of the
-model. There are three model types: **notebook**, **file**, and **directory**.
-
-- ``notebook`` models
- - The ``format`` field is always ``"json"``.
- - The ``mimetype`` field is always ``None``.
- - The ``content`` field contains a
- :class:`nbformat.notebooknode.NotebookNode` representing the .ipynb file
- represented by the model. See the `NBFormat`_ documentation for a full
- description.
-
-- ``file`` models
- - The ``format`` field is either ``"text"`` or ``"base64"``.
- - The ``mimetype`` field is ``text/plain`` for text-format models and
- ``application/octet-stream`` for base64-format models.
- - The ``content`` field is always of type ``unicode``. For text-format
- file models, ``content`` simply contains the file's bytes after decoding
- as UTF-8. Non-text (``base64``) files are read as bytes, base64 encoded,
- and then decoded as UTF-8.
-
-- ``directory`` models
- - The ``format`` field is always ``"json"``.
- - The ``mimetype`` field is always ``None``.
- - The ``content`` field contains a list of :ref:`content-free`
- models representing the entities in the directory.
-
-.. note::
-
- .. _contentfree:
-
- In certain circumstances, we don't need the full content of an entity to
- complete a Contents API request. In such cases, we omit the ``mimetype``,
- ``content``, and ``format`` keys from the model. This most commonly occurs
- when listing a directory, in which circumstance we represent files within
- the directory as content-less models to avoid having to recursively traverse
- and serialize the entire filesystem.
-
-**Sample Models**
-
-.. code-block:: python
-
- # Notebook Model with Content
- {
- 'content': {
- 'metadata': {},
- 'nbformat': 4,
- 'nbformat_minor': 0,
- 'cells': [
- {
- 'cell_type': 'markdown',
- 'metadata': {},
- 'source': 'Some **Markdown**',
- },
- ],
- },
- 'created': datetime(2015, 7, 25, 19, 50, 19, 19865),
- 'format': 'json',
- 'last_modified': datetime(2015, 7, 25, 19, 50, 19, 19865),
- 'mimetype': None,
- 'name': 'a.ipynb',
- 'path': 'foo/a.ipynb',
- 'type': 'notebook',
- 'writable': True,
- }
-
- # Notebook Model without Content
- {
- 'content': None,
- 'created': datetime.datetime(2015, 7, 25, 20, 17, 33, 271931),
- 'format': None,
- 'last_modified': datetime.datetime(2015, 7, 25, 20, 17, 33, 271931),
- 'mimetype': None,
- 'name': 'a.ipynb',
- 'path': 'foo/a.ipynb',
- 'type': 'notebook',
- 'writable': True
- }
-
-
-API Paths
-~~~~~~~~~
-.. _apipaths:
-
-ContentsManager methods represent the locations of filesystem resources as
-**API-style paths**. Such paths are interpreted as relative to the root
-directory of the notebook server. For compatibility across systems, the
-following guarantees are made:
-
-* Paths are always ``unicode``, not ``bytes``.
-* Paths are not URL-escaped.
-* Paths are always forward-slash (/) delimited, even on Windows.
-* Leading and trailing slashes are stripped. For example, ``/foo/bar/buzz/``
- becomes ``foo/bar/buzz``.
-* The empty string (``""``) represents the root directory.
-
-
-Writing a Custom ContentsManager
---------------------------------
-
-The default ContentsManager is designed for users running the notebook as an
-application on a personal computer. It stores notebooks as .ipynb files on the
-local filesystem, and it maps files and directories in the Notebook UI to files
-and directories on disk. It is possible to override how notebooks are stored
-by implementing your own custom subclass of ``ContentsManager``. For example,
-if you deploy the notebook in a context where you don't trust or don't have
-access to the filesystem of the notebook server, it's possible to write your
-own ContentsManager that stores notebooks and files in a database.
-
-
-Required Methods
-~~~~~~~~~~~~~~~~
-
-A minimal complete implementation of a custom
-:class:`~manager.ContentsManager` must implement the following
-methods:
-
-.. autosummary::
- ContentsManager.get
- ContentsManager.save
- ContentsManager.delete_file
- ContentsManager.rename_file
- ContentsManager.file_exists
- ContentsManager.dir_exists
- ContentsManager.is_hidden
-
-You may be required to specify a Checkpoints object, as the default one,
-``FileCheckpoints``, could be incompatible with your custom
-ContentsManager.
-
-Customizing Checkpoints
------------------------
-.. currentmodule:: jupyter_server.services.contents.checkpoints
-
-Customized Checkpoint definitions allows behavior to be
-altered and extended.
-
-The ``Checkpoints`` and ``GenericCheckpointsMixin`` classes
-(from :mod:`jupyter_server.services.contents.checkpoints`)
-have reusable code and are intended to be used together,
-but require the following methods to be implemented.
-
-.. autosummary::
- Checkpoints.rename_checkpoint
- Checkpoints.list_checkpoints
- Checkpoints.delete_checkpoint
- GenericCheckpointsMixin.create_file_checkpoint
- GenericCheckpointsMixin.create_notebook_checkpoint
- GenericCheckpointsMixin.get_file_checkpoint
- GenericCheckpointsMixin.get_notebook_checkpoint
-
-No-op example
-~~~~~~~~~~~~~
-
-Here is an example of a no-op checkpoints object - note the mixin
-comes first. The docstrings indicate what each method should do or
-return for a more complete implementation.
-
-.. code-block:: python
-
- class NoOpCheckpoints(GenericCheckpointsMixin, Checkpoints):
- """requires the following methods:"""
- def create_file_checkpoint(self, content, format, path):
- """ -> checkpoint model"""
- def create_notebook_checkpoint(self, nb, path):
- """ -> checkpoint model"""
- def get_file_checkpoint(self, checkpoint_id, path):
- """ -> {'type': 'file', 'content': , 'format': {'text', 'base64'}}"""
- def get_notebook_checkpoint(self, checkpoint_id, path):
- """ -> {'type': 'notebook', 'content':