Recap and assembling the final application#

So far we have looked at the different parts of the application in isolation, but now it’s time to put them all together and assemble a complete application.

Final application#

app.py#
 1from dataclasses import dataclass
 2from typing import List, Optional
 3
 4from litestar import Litestar, get, post, put
 5from litestar.exceptions import NotFoundException
 6from litestar.params import FromPath, FromQuery
 7
 8
 9@dataclass
10class TodoItem:
11    title: str
12    done: bool
13
14
15TODO_LIST: List[TodoItem] = [
16    TodoItem(title="Start writing TODO list", done=True),
17    TodoItem(title="???", done=False),
18    TodoItem(title="Profit", done=False),
19]
20
21
22def get_todo_by_title(todo_name: str) -> TodoItem:
23    for item in TODO_LIST:
24        if item.title == todo_name:
25            return item
26    raise NotFoundException(detail=f"TODO {todo_name!r} not found")
27
28
29@get("/")
30async def get_list(done: FromQuery[Optional[bool]] = None) -> List[TodoItem]:
31    if done is None:
32        return TODO_LIST
33    return [item for item in TODO_LIST if item.done == done]
34
35
36@post("/")
37async def add_item(data: TodoItem) -> List[TodoItem]:
38    TODO_LIST.append(data)
39    return TODO_LIST
40
41
42@put("/{item_title:str}")
43async def update_item(item_title: FromPath[str], data: TodoItem) -> List[TodoItem]:
44    todo_item = get_todo_by_title(item_title)
45    todo_item.title = data.title
46    todo_item.done = data.done
47    return TODO_LIST
48
49
50app = Litestar([get_list, add_item, update_item])
app.py#
 1from dataclasses import dataclass
 2from typing import Optional
 3
 4from litestar import Litestar, get, post, put
 5from litestar.exceptions import NotFoundException
 6from litestar.params import FromPath, FromQuery
 7
 8
 9@dataclass
10class TodoItem:
11    title: str
12    done: bool
13
14
15TODO_LIST: list[TodoItem] = [
16    TodoItem(title="Start writing TODO list", done=True),
17    TodoItem(title="???", done=False),
18    TodoItem(title="Profit", done=False),
19]
20
21
22def get_todo_by_title(todo_name: str) -> TodoItem:
23    for item in TODO_LIST:
24        if item.title == todo_name:
25            return item
26    raise NotFoundException(detail=f"TODO {todo_name!r} not found")
27
28
29@get("/")
30async def get_list(done: FromQuery[Optional[bool]] = None) -> list[TodoItem]:
31    if done is None:
32        return TODO_LIST
33    return [item for item in TODO_LIST if item.done == done]
34
35
36@post("/")
37async def add_item(data: TodoItem) -> list[TodoItem]:
38    TODO_LIST.append(data)
39    return TODO_LIST
40
41
42@put("/{item_title:str}")
43async def update_item(item_title: FromPath[str], data: TodoItem) -> list[TodoItem]:
44    todo_item = get_todo_by_title(item_title)
45    todo_item.title = data.title
46    todo_item.done = data.done
47    return TODO_LIST
48
49
50app = Litestar([get_list, add_item, update_item])
app.py#
 1from dataclasses import dataclass
 2
 3from litestar import Litestar, get, post, put
 4from litestar.exceptions import NotFoundException
 5from litestar.params import FromPath, FromQuery
 6
 7
 8@dataclass
 9class TodoItem:
10    title: str
11    done: bool
12
13
14TODO_LIST: list[TodoItem] = [
15    TodoItem(title="Start writing TODO list", done=True),
16    TodoItem(title="???", done=False),
17    TodoItem(title="Profit", done=False),
18]
19
20
21def get_todo_by_title(todo_name: str) -> TodoItem:
22    for item in TODO_LIST:
23        if item.title == todo_name:
24            return item
25    raise NotFoundException(detail=f"TODO {todo_name!r} not found")
26
27
28@get("/")
29async def get_list(done: FromQuery[bool | None] = None) -> list[TodoItem]:
30    if done is None:
31        return TODO_LIST
32    return [item for item in TODO_LIST if item.done == done]
33
34
35@post("/")
36async def add_item(data: TodoItem) -> list[TodoItem]:
37    TODO_LIST.append(data)
38    return TODO_LIST
39
40
41@put("/{item_title:str}")
42async def update_item(item_title: FromPath[str], data: TodoItem) -> list[TodoItem]:
43    todo_item = get_todo_by_title(item_title)
44    todo_item.title = data.title
45    todo_item.done = data.done
46    return TODO_LIST
47
48
49app = Litestar([get_list, add_item, update_item])

Recap#

app.py#
29@get("/")
30async def get_list(done: FromQuery[Optional[bool]] = None) -> List[TodoItem]:
31    if done is None:
32        return TODO_LIST
33    return [item for item in TODO_LIST if item.done == done]

A route handler set up with get("/") responds to GET requests and returns a list of all items on our TODO list. The optional query parameter done allows filtering the items by status. It is declared as FromQuery[bool | None]: the FromQuery marker tells Litestar to read the value from the URL query string, the inner bool triggers conversion, and wrapping it in Optional (combined with the = None default) makes it optional.

app.py#
36@post("/")
37async def add_item(data: TodoItem) -> List[TodoItem]:
38    TODO_LIST.append(data)
39    return TODO_LIST

A route handler set up with post("/") responds to POST requests and adds an item to the TODO list. The data for the new item is received via the request data, which the route handler accesses by specifying the data parameter. The type annotation of TodoItem means the request data will parsed as JSON, which is then used to create an instance of the TodoItem dataclass, which - finally - gets passed into the function.

app.py#
42@put("/{item_title:str}")
43async def update_item(item_title: FromPath[str], data: TodoItem) -> List[TodoItem]:
44    todo_item = get_todo_by_title(item_title)
45    todo_item.title = data.title
46    todo_item.done = data.done
47    return TODO_LIST

A route handler set up with put("/{item_title:str}"), making use of a path parameter, responds to PUT requests on the path /some todo title, where some todo title is the title of the TodoItem you wish to update. The handler declares item_title: FromPath[str] to receive the captured value; the FromPath marker tells Litestar this kwarg comes from the URL path, and the matching name (item_title) lines it up with the {item_title:str} slot. The :str suffix in the path pattern means the value is treated as a string. Additionally, this route handler receives data of a TodoItem the same way as the POST handler.

app.py#
50app = Litestar([get_list, add_item, update_item])

An instance of Litestar is created, including the previously defined route handlers. This app can now be served using an ASGI server like uvicorn, which can be conveniently done using Litestar’s CLI by executing the litestar run command.

Next steps#

This tutorial covered some of the fundamental concepts of Litestar. For a more in-depth explanation of these topics, refer to the Usage Guide.