OrderTogether is a web application for coordinating group food delivery orders within a shared building or office. Users create a group order for a specific delivery service (Wolt, Tenbis, Mishloha, etc.), and others can join before a deadline. The goal is to reduce individual delivery fees by consolidating orders.
- Group order management -- create, join, leave, lock, and track delivery orders through their full lifecycle (open, locked, ordered, delivered, cancelled).
- In-App Chat & Notifications -- Every order has a dedicated chat room. You receive real-time notifications when users join, leave, comment, upload receipts, or when the order status changes.
- Receipt Uploads -- The order creator can upload a digital receipt for everyone in the group to view and download, ensuring transparent fee splits.
- Admin & Superuser Dashboard -- Admins can manage available buildings, food tags, and delivery services directly via the UI. Superusers can promote or demote Admins from an internal user management dashboard.
- Multi-service support -- built-in support for Wolt, Tenbis, and Mishloha, with extensible service definitions stored in the database.
- QR code generation -- for Wolt orders, a scannable QR code linking to the group order is generated locally on the client. No data is sent to external services.
- Theme system -- six visual themes (Midnight, Dark, Light, Sunset, Ocean, Forest) with full CSS variable architecture. Theme preference is persisted in local storage.
- Authentication -- JWT-based registration and login with bcrypt password hashing and automatic token refresh. Optionally supports Windows Active Directory (LDAP) logins.
- "Where should we eat?" Polling -- Can't decide where to order from? Create a poll and let your team vote between custom options or specific delivery services. Then instantly launch a shared order with the winning choice!
- 1-Click Reorder -- easily duplicate any past group order to save time setting up your regular Thursday Team Lunch.
- Live Social Feed -- your dashboard includes a dynamic real-time social timeline showing any new orders created, polls started, users joining an order, or status changes.
- Real-time countdown -- live deadline timers on the order board with visual urgency indicators.
| Layer | Technology |
|---|---|
| Backend | Python 3.12+, FastAPI, SQLAlchemy (async), aiosqlite, asyncpg, Pydantic v2 |
| Frontend | React 18, Vite, React Router, qrcode.react, Lucide icons |
| Auth | JWT (python-jose), bcrypt, ldap3 |
| Database | SQLite (default via data/ dir) or PostgreSQL (optional via .env) |
| Docker | Multi-stage containerization with Docker Compose and Nginx |
The easiest way to run the application is via Docker Compose. It automatically spins up a robust PostgreSQL database, the Backend API, and the pre-built Nginx Frontend.
-
Create a
backend/.envfile with your preferred configurations. For a quick start:# backend/.env POSTGRES_USER="db_admin" POSTGRES_PASSWORD="secure_password" POSTGRES_DB="ordertogether" SUPERUSER_USERNAME="admin" SECRET_KEY="production-secret-here"
-
Run the deployment (this will pull the required images):
docker-compose up -d
-
Access the application:
- Frontend React App:
http://localhost:8080 - Backend API Docs:
http://localhost:8000/docs
- Frontend React App:
If you want to run the containers manually without docker-compose, you must first create a shared bridge network so the containers can resolve each other by name. Without this, the frontend will crash on startup with an nginx: [emerg] host not found in upstream "backend" error.
# 1. Create a dedicated network
docker network create ordertogether_net
# 2. Run the backend container (requires environment variables from .env to be passed if needed)
docker run -d --name backend --network ordertogether_net \
-v ./backend/data:/app/data \
poortuna/ordertogether/ordertogether-backend:v1.0.0
# 3. Run the frontend container
# We pass BACKEND_HOST=backend to tell the Nginx proxy where to route /api/ traffic.
docker run -it --rm --name frontend --network ordertogether_net \
-e BACKEND_HOST=backend \
-p 8080:8080 poortuna/ordertogether/ordertogether-frontend:v1.0.0Alternatively, if your backend is running externally or on your host machine (and you aren't using a docker network), you can point the frontend to it directly:
docker run -it --rm -p 8080:8080 -e BACKEND_HOST=host.docker.internal -e BACKEND_PORT=8000 poortuna/ordertogether/ordertogether-frontend:v1.0.0- Python 3.11+
- Node.js 18+
- uv (Python package manager)
cd backend
uv sync
uv run uvicorn main:app --reloadThe API server starts at http://127.0.0.1:8000. On first startup, a local active SQLite database will be created inside the backend/data/ folder, and it will be seeded with default services.
In a separate terminal:
cd frontend
npm install
npm run devThe development server starts at http://localhost:5173, and natively proxies API requests to the backend server.
The backend reads environment variables from backend/.env.
By default, the application runs on SQLite in the ./data directory. To seamlessly switch to a production-ready PostgreSQL instance:
| Variable | Example / Default | Description |
|---|---|---|
POSTGRES_HOST |
127.0.0.1 |
Triggers Postgres mode instead of SQLite. |
POSTGRES_PORT |
5432 |
Required if POSTGRES_HOST is set. |
POSTGRES_USER |
admin |
Postgres Username |
POSTGRES_PASSWORD |
password123 |
Postgres Password |
POSTGRES_DB |
ordertogether |
Target logical Database Name |
DATABASE_URL |
(overrides everything) | A raw, full postgresql:// URI string. |
To initialize your first administrator without needing direct database access, configure a Superuser. A Superuser can instantly elevate any standard user account to an Admin via the in-app Admin Dashboard.
SUPERUSER_USERNAME: Give a specific local or LDAP user permanent Superuser privileges when they sign in (e.g.SUPERUSER_USERNAME="my_admin_handle").LDAP_SUPERUSER_GROUP: If LDAP is active, any user whose Active DirectorymemberOftraits partially matches this DN (e.g.CN=IT-Admins) will automatically be promoted to a Superuser.
You can sync login with your Windows domain (Active Directory) so users sign in with their AD credentials. Local logins seamlessly continue to function side-by-side.
LDAP_ENABLED=trueLDAP_URL— e.g.ldap://your-dc.company.comorldaps://...LDAP_BASE_DN— e.g.DC=company,DC=comLDAP_BIND_DN— service account DN for searching (optional if anonymous bind is allowed), e.g.CN=svc-ldap,OU=Service Accounts,DC=company,DC=comLDAP_BIND_PASSWORD— password for the service accountLDAP_USER_SEARCH_ATTRIBUTE— attribute to match the login value, usuallyuserPrincipalName(email) orsAMAccountName(Windows username)LDAP_USER_SEARCH_ATTRIBUTE_ALT— optional second attribute so users can log in with either email or username (e.g.sAMAccountNameif primary isuserPrincipalName)LDAP_USER_SEARCH_FILTER— optional, e.g.(objectClass=user)or restrict to an OU