Making the list interactive ============================ So far, our TODO list application is not very useful, since it's static. You can't update items, nor add or remove them. Receiving incoming data ----------------------- Let's start by implementing a route handler that handles the creation of new items. In the previous step you used the ``get`` decorator, which responds to the ``GET`` HTTP method. In this case we want to react to ``POST`` requests, so we are going to use the corresponding ``post`` decorator. .. literalinclude:: /examples/todo_app/create/dict.py :language: python :linenos: Request data can be received via the ``data`` keyword. Litestar will recognize this, and supply the data being sent with the request via this parameter. As with the query parameters in the previous chapter, we use the type annotations to configure what type of data we expect to receive, and set up validation. In this case, Litestar will expect request data in the form of JSON and use the type annotation we gave it to convert it into the correct format. .. seealso:: * :doc:`/usage/requests` Using the interactive documentation to test a route ++++++++++++++++++++++++++++++++++++++++++++++++++++ Since our example now uses the ``POST`` HTTP method, you can no longer simply visit the URL in our browser and get a response. Instead, you can use the interactive documentation to send a ``POST`` request. Because of the OpenAPI schema generated by Litestar, Swagger will know exactly what kind of data to send. In this example, it will send a simple JSON object. .. figure:: images/swagger-post-dict-response.png Sending a sample request to our ``add_item`` route reveals a successful response Improving the example with dataclasses ++++++++++++++++++++++++++++++++++++++ As in the previous chapter, this too can be improved by using :doc:`dataclasses ` instead of plain dicts. .. literalinclude:: /examples/todo_app/create/dataclass.py :language: python :linenos: This is not only easier on the eyes and adds more structure to the code, but also gives better interactive documentation; it will now present us with the field names and default values for the dataclass we have defined: .. figure:: images/swagger-dict-vs-dataclass.png Documentation for the ``add_item`` route with ``data`` typed as a ``dict`` vs ``dataclass`` Using a dataclass also gives you better validation: omitting a key such as ``title`` will result in a useful error response: .. figure:: images/swagger-dataclass-bad-body.png Sending a request without a ``title`` key fails Create dynamic routes using path parameters ------------------------------------------- The next task on the list is updating an item's status. For this, a way to refer to a specific item on the list is needed. This could be done using query parameters, but there's an easier, and more semantically coherent way of expressing this: path parameters. .. code-block:: python @get("/{name:str}") async def greeter(name: FromPath[str]) -> str: return "Hello, " + name So far all the paths in your application are static, meaning they are expressed by a constant string which does not change. In fact, the only path used so far is ``/``. Path parameters allow you to construct dynamic paths and later refer to the dynamically captured parts. This may sound complex at first, but it's actually quite simple; you can think of it as a regular expression that's being used on the requested path. Path parameters consist of two parts: an expression inside the route's path describing the slot (e.g. ``{name:str}``), and a matching function parameter on the route handler annotated with :data:`~.params.FromPath` (e.g. ``name: FromPath[str]``), which will receive the captured value. In the above example, a path parameter ``name:str`` is declared, which means that now, a request to the path ``/john`` can be made, and the ``greeter`` function will be called as ``greeter(name="john")``, similar to how query parameters are injected. .. tip:: Just like query parameters, path parameters can convert and validate their values as well. This is configured using the ``:type`` colon annotation in the URL pattern. For example, ``value:str`` will receive values as a string, while ``value:int`` will try to convert it into an integer. A full list of supported types can be found here: :ref:`usage/routing/parameters:supported path parameter types` By using this pattern and combining it with those from the earlier section about receiving data you can now set up a route handler that takes in the title of a TODO item, an updated item in form of a dataclass instance, and updates the item in the list. .. literalinclude:: /examples/todo_app/update.py :language: python .. seealso:: * :ref:`usage/routing/parameters:path parameters`