# In-memory storage for invoices
invoices = [
    Invoice(id=1, customer_uid="4r3dd", amount=250.50, status="Paid"),
    Invoice(id=2, customer_uid="f3f3f3f", amount=150.00, status="Pending"),
]

@app.get("/invoices", response_model=List[Invoice])
async def list_invoices():
    return invoices
```

### Django REST framework

If what you care about is security, reliability, and maturity, [Django REST framework (DRF)](https://www.django-rest-framework.org/) is what you want. Django is the most mature Python framework and rose to prominence thanks to its abstractions of the tedious but essential parts of backend development: authentication, authorization, logging, multiple database connections, caching, testing, and much more. 

However, this abstraction comes with trade-offs. Django is not especially flexible or lightweight, and its enforced Model-View-Template (MVT) structure can feel verbose and rigid compared to more modern frameworks. However, if you embrace its design principles, Django can be one of the most stable and effective frameworks you've ever used.

When it comes to async support, DRF does not currently support async functionality. This limitation means you cannot create async API views or viewsets using DRF, as its core features – like serializers, authentication, permissions, and other utilities – are not designed to work asynchronously. 

Third-party package [ADRF (Async DRF)](https://github.com/em1208/adrf) adds async support, but it's not officially supported and may not be stable for production. That undermines the core value of Django REST framework: stability.

To create an API with DRF, you need to define a model first.   

```python filename="models.py"
from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name
```

Then, you need to define a serializer that will convert the Python object retrieved from the Django ORM to a JSON object and vice versa.   

```python filename="serializers.py"
from rest_framework import serializers
from .models import Item

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ['id', 'name', 'description', 'price', 'created_at']
``` 
 
Next, you need to write a view (or in standard terms, controller) to handle the API logic, in this case, listing.

```python filename="views.py"
from rest_framework.generics import ListCreateAPIView
from .models import Item
from .serializers import ItemSerializer

class ItemListCreateView(ListCreateAPIView):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer
```   

Finally, you need to register the view in a `urls.py` file.  

```python
from django.urls import path
from .views import ItemListCreateView

urlpatterns = [
    path('items/', ItemListCreateView.as_view(), name='item-list-create'),
]
```

This example illustrates how verbose Django can be. But by following its well-documented architecture, you ensure your application is robust and scalable while following proven design principles. 

### Tornado: Pure async logic

Tornado is a lightweight framework built entirely around asynchronous programming, making it ideal for building APIs where non-blocking I/O is critical, like WebSocket-based applications or systems with high-concurrency needs. If you don't have the immediate pressure of needing an extensive feature set or an existing ecosystem, Tornado can be an excellent choice for applications requiring pure async workflows.  

```python filename="app.py"
from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler
import json