Backend Development¶
The Meet backend is a Django 5.x application written in Python 3.13+.
Tech stack¶
| Component | Technology |
|---|---|
| Framework | Django 5.x + Django REST Framework |
| Language | Python 3.13+ |
| Package manager | uv |
| Auth | mozilla-django-oidc |
| LiveKit integration | livekit-api (Python SDK) |
| Async tasks | Celery |
| Testing | pytest + pytest-django |
| Linting | Ruff |
Running the backend¶
# Start the backend container (dev mode with hot reload)
docker compose up -d app-dev
# Django shell
docker compose exec app-dev python manage.py shell
# Apply migrations
docker compose exec app-dev python manage.py migrate
# Create superuser
docker compose exec app-dev python manage.py createsuperuser
Settings¶
The backend uses django-configurations. The settings class is selected via DJANGO_CONFIGURATION:
Development— debug mode, relaxed securityDemo— production-like with demo dataProduction— full production settings
All settings live in src/backend/meet/settings.py as a single file using django-configurations class inheritance.
Key models¶
# core/models.py
class Room(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
slug = models.SlugField(unique=True)
name = models.CharField(max_length=255)
access_level = models.CharField(...) # public, authenticated, restricted
configuration = models.JSONField(default=dict)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
class Recording(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
status = models.CharField(...) # initiated, active, stopped, saved
mode = models.CharField(...) # room_composite, etc.
storage_key = models.CharField(...) # S3 object key
After model changes, create and apply migrations:
docker compose exec app-dev python manage.py makemigrations
docker compose exec app-dev python manage.py migrate
API views¶
Views use Django REST Framework ViewSets:
class RoomViewSet(viewsets.ModelViewSet):
serializer_class = RoomSerializer
permission_classes = [IsAuthenticated, IsRoomMemberOrPublic]
def get_queryset(self):
return Room.objects.filter(
Q(access_level="public") | Q(accesses__user=self.request.user)
)
LiveKit token generation¶
The backend generates short-lived JWTs for clients:
from livekit import api
token = (
api.AccessToken(settings.LIVEKIT_CONFIGURATION["api_key"],
settings.LIVEKIT_CONFIGURATION["api_secret"])
.with_identity(str(user.id))
.with_name(user.name)
.with_grants(api.VideoGrants(room_join=True, room=room.slug))
.to_jwt()
)
Celery tasks¶
Async tasks are defined in core/tasks.py and dispatched with .delay():
from meet.celery_app import app
@app.task
def send_recording_notification(recording_id: str):
recording = Recording.objects.get(id=recording_id)
# send email with download link
Running tests¶
# All backend tests
make test-back
# Specific file
docker compose exec app-dev pytest core/tests/test_rooms.py
# With coverage
docker compose exec app-dev pytest --cov=meet --cov-report=html
# Single test
docker compose exec app-dev pytest core/tests/test_rooms.py::TestRoomCreate::test_authenticated
Code style¶
# Lint and format (Ruff)
make lint-back
# Or directly
docker compose exec app-dev ruff check .
docker compose exec app-dev ruff format .
Adding dependencies¶
Django admin¶
Available at /admin/. Key sections:
- Rooms — list, configure rooms
- Recordings — view recording state and storage keys
- Users — manage accounts
- Applications — external app integrations