A Dockerized full-stack web application to randomly pick students from classes.
Built as a clean DevOps-style setup with separate frontend and backend services, containerized via Docker and orchestrated using Docker Compose.
- Vue 3
- Vite
- Nginx (production build + reverse proxy)
- Node.js
- Express
- SQLite (persistent via Docker volume)
- Docker & Docker Compose
- GitHub Actions
- GitHub Container Registry (GHCR)
Browser
↓
Frontend (Vue + Nginx) :8324
↓ /api/*
Backend (Node + Express) :3000
↓
SQLite DB (Docker volume)
- The frontend does not talk directly to the backend via localhost.
- All API requests are proxied through Nginx using
/api/*. - The backend service is internal only (not exposed to the host).
- SQLite data persists using a Docker volume.
- Docker
- Docker Compose
docker compose up --build- Frontend: http://localhost:8324
- API (via frontend proxy): http://localhost:8324/api
All API endpoints are available under /api/*.
GET /api/classes
POST /api/classes
Content-Type: application/json
{ "name": "Class A" }
PUT /api/classes/:id
Content-Type: application/json
{ "name": "Updated Class Name" }
DELETE /api/classes/:id
GET /api/students/class/:classId
POST /api/students
Content-Type: application/json
{ "name": "Max Mustermann", "class_id": 1 }
PUT /api/students/:id
Content-Type: application/json
{ "name": "Updated Name" }
DELETE /api/students/:id
POST /api/pick
Content-Type: application/json
{ "class_id": 1, "count": 1 }
Response: [ { "id": 3, "name": "Max Mustermann" } ]
- SQLite database is stored inside
/app/data/db.sqlite - This path is mounted to a Docker volume
- Data survives container restarts
- Each service has its own Dockerfile
- Images are built and pushed automatically via GitHub Actions
- Images are published to GitHub Container Registry (GHCR)
- Docker Compose works both locally and with published images
- Backend is intentionally not exposed to the host
- Frontend Nginx acts as a reverse proxy for the API
- This setup follows real-world containerized full-stack best practices