Custom Handlers with parameters

Hello all,

How do I define a custom handler with parameters as part of a JupyterLab extension? For my extension, I wrote most of the logic as a python package, now I want to integrate it into JupyterLab by defining custom API handlers, and have the GUI components in the extension to make calls to the extension API based on the user’s inputs. I’ve read the documentation and some code examples in GH, but I haven’t figured out how to do that. I am familiar with the following example code and explanation:

class HelloWorldHandler(APIHandler):

    def __init__(self, *args, **kwargs):
        self.extra = kwargs.pop('extra')
        ...

def load_jupyter_server_extension(nb_server_app):

    ...
    web_app.add_handlers(host_pattern,
        [
           (route_pattern, HelloWorldHandler, {"extra": nb_server_app.extra})
        ])

However, I am very new to Jupyter and TypeScript, and the following is not clear to me.

  1. How to define an API handler with custom parameters. I appreciate the explanation in the documentation, but do you know any complete code example I can take a look at?
  2. In the front end (TypeScript). How do I use the extension API as part of a plugin for passing values provided by the used as parameters to the API end point?
  3. I see many code examples defining handlers for GET, but is it possible/desirable to define methods for POST/UPDATE?
1 Like

This is covered best by working interactively with cookiecutter-extension-ts:

pip install cookiecutter
cookiecutter https://github.com/jupyterlab/extension-cookiecutter-ts --checkout 3.0
# choose kind: server

This should leave a ready-to-develop extension, with an example of using get, inherited from RequestHandler, with an example of using it browser-side as well.

The get method can access ?<param>=<value> self.get_query_argument("<param">). post works the same way, using get_body_argument (and also get_query_argument, if desired).

Both use regular expression groups in add_handlers to capture /url/params/, and these are passed by position (or ideally, name) to the various methods.

Another path altogether: if there’s any reason your users would benefit from programmatic access to the live state of your software, using the widget-ts-cookiecutter skips the whole server thing. This is a much more composable design, though the complexity of working with widgets is a bit higher, as you still need a proper jupyterlab plugin. This also has the benefit of not requiring a server restart, just a kernel restart, after installing the package, and can be used transparently in other frontends like JupyterLite, often without changes, and, soon voila-dashboards.

2 Likes

Thanks for the detailed explanation!
I actually started working on my extension using cookiecutter for the server extension. With the explanation about how the URL queries work and how to define parameters in the handlers, I can move forward. I also appreciate your explanation about the widget-ts-cookiecutter; I will keep that approach in mind as I develop the extension.

For those who may need a code example. I provide one here:

# handler.py 

""" API handler that requires an argument named 'param1' for GET """
class RouteHandler(APIHandler):
    @tornado.web.authenticated
    def  get(self):
        param = self.get.query_argument("param1") # this defines the param. name.
        self.finish(json.dumps({
            "data": f"Query example! param1 is  set to: {param}!!!"
        }))

""" URL query example"
https://<your-ip>/<you-extension-endpoint>?param1=<value>
"""
2 Likes