A RESTful API backend for Coderr, a service marketplace connecting freelance developers with clients. Built with Django and Django REST Framework, it handles user accounts, offers, orders and reviews, and is designed to be consumed by a separate frontend application.
🔗 Live API: coderr.marc-schaar.com/api · 📁 Repository: github.com/Marc-Schaar/coderr_backend · 🌐 Portfolio: marc-schaar.com
🇩🇪 Kurzfassung auf Deutsch: Coderr ist die REST-API für einen Freelancer-Marktplatz, gebaut mit Django und Django REST Framework — Nutzerkonten, Angebote, Bestellungen und Bewertungen mit feingranularen Berechtigungen. Läuft containerisiert (Docker), mit vollautomatisierter CI/CD-Pipeline: Tests und Deployment über GitHub Actions. Live-API →
This project was built as part of the Developer Akademie backend course and now serves as a portfolio piece, with a full CI/CD pipeline (automated tests + automated deployment via GitHub Actions).
- Features
- Tech Stack
- Getting Started (Local Setup)
- Configuration (Environment Variables)
- Running Tests
- API Usage
- CI/CD Pipeline
- Deployment
- Project Structure
- Notes
- About
- User registration and token-based authentication
- Business and customer profile management
- CRUD operations for offers and offer details
- Order management for customers and businesses
- Review system for business users
- Fine-grained permissions so only authorized users can access or modify data
- Filtering, searching and pagination for offers and reviews
- Automated tests and automated deployment via GitHub Actions
- Python 3.10+
- Django 5.2
- Django REST Framework
- django-filter for filtering/searching
- python-dotenv for loading configuration from a
.envfile - SQLite for local development, PostgreSQL in production (switches automatically based on
DEBUG) - GitHub Actions for CI/CD
- gunicorn + WhiteNoise serving the app, packaged with Docker, deployed via
docker compose
- Python 3.10 or newer
- Git
git clone https://github.com/Marc-Schaar/coderr_backend.git
cd coderr_backendpython3 -m venv env
source env/bin/activate # macOS/Linux
# .\env\Scripts\activate # Windowspip install -r requirements.txtcp .env.example .env # Windows: copy .env.example .envThe defaults in .env.example already work for local development — no changes needed to just get started. See Configuration for details.
python manage.py migratepython manage.py createsuperuserpython manage.py runserverThe API is now available at http://127.0.0.1:8000/api/.
Alternative: Docker The
Dockerfilebuilds a production-style image (gunicorn + WhiteNoise). For local development you can still run Django's dev server inside it by mounting the source and overriding the command:docker build -t coderr . docker run -it -v "$(pwd):/usr/src/app" -w /usr/src/app -p 8000:8000 coderr python manage.py runserver 0.0.0.0:8000(see
run_django.shfor the same command as a script)For a production-like run instead (gunicorn,
DEBUG=False, needs a reachable PostgreSQL via theDB_*variables in your.env):docker compose -f docker-compose.prod.yml up --build
Settings that differ between local development and production (secret key, debug mode, allowed hosts) are read from environment variables via python-dotenv, instead of being hardcoded in core/settings.py. On startup, Django loads a .env file from the project root, if present.
.env.example documents every variable and is safe to commit — .env itself is git-ignored and must never be committed, since it holds real secrets in production.
| Variable | Description | Local default | Production example |
|---|---|---|---|
SECRET_KEY |
Django's cryptographic signing key | insecure placeholder | a long random string (see below) |
DEBUG |
Enables Django's debug mode and detailed error pages | True |
False |
ALLOWED_HOSTS |
Comma-separated hostnames the server may respond to | 127.0.0.1,localhost |
coderr.marc-schaar.com |
DB_NAME |
PostgreSQL database name (only used when DEBUG=False) |
— | coderr |
DB_USER |
PostgreSQL user (only used when DEBUG=False) |
— | coderr |
DB_PASSWORD |
PostgreSQL password (only used when DEBUG=False) |
— | (secret) |
DB_HOST |
PostgreSQL host (only used when DEBUG=False) |
— | localhost |
DB_PORT |
PostgreSQL port (only used when DEBUG=False) |
— | 5432 |
CSRF_TRUSTED_ORIGINS |
Comma-separated origins trusted for unsafe requests (e.g. admin login) (only needed when DEBUG=False, behind a reverse proxy) |
— | https://coderr.marc-schaar.com |
GUNICORN_WORKERS |
Number of gunicorn worker processes (Docker/production only) | — | 3 |
Generate a real production secret key with:
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"If no .env file exists (or a variable is missing from it), the app falls back to the local-development defaults above, so a fresh clone still runs out of the box.
The database is selected automatically based on DEBUG — no separate switch to remember:
DEBUG=True(local development): always uses a local SQLite file (db.sqlite3), zero setup required.DEBUG=False(production): always uses PostgreSQL, configured via theDB_*variables above. Thepsycopg2-binarydriver is already included inrequirements.txt.
This means the production database must exist and be reachable before migrations run — see Deployment.
The project uses Django's built-in test runner. Each app ships its own test suite (test_app/).
python manage.py testEvery push and pull request automatically runs this same command via GitHub Actions — see CI/CD Pipeline.
- Base URL:
http://127.0.0.1:8000/api/(local) /https://coderr.marc-schaar.com/api/(live)
The API uses token-based authentication. Obtain a token via the login endpoint, then include it in every subsequent request:
Authorization: Token <your_token>
- All endpoints except registration and login require authentication.
- Only business users can create offers.
- Only customers can place orders and write reviews.
The project is fully automated with two GitHub Actions workflows in .github/workflows/:
Runs on every push and on every pull request targeting master, with two jobs:
test: checks out the code, sets up Python 3.12, installs dependencies fromrequirements.txt, runspython manage.py test.docker-build: builds theDockerfileimage and runs it as a smoke test (fallback SQLite settings, no.envneeded) to confirm gunicorn and WhiteNoise actually come up inside the container. This is the only place the image gets built and run before it ships to production — there's no local Docker available in this project's dev environment (Windows-on-Mac without virtualization).
Runs on every push to master (and can also be triggered manually via Actions → Deploy → Run workflow):
- Calls
tests.ymland only continues if all tests pass - Connects to the production server over SSH
- Pulls the latest
masterbranch (git pull --ff-only, so it never overwrites diverging local changes) - Rebuilds and restarts the app container with
docker compose -f docker-compose.prod.yml up -d --build— this also applies pending migrations (runs on container start, seeentrypoint.sh) and refreshes static files (collected during the image build) - Prunes dangling images left over from the previous build
This means: merging to master automatically ships to production, but only if the tests are green.
Deployment targets a Linux server (e.g. an Uberspace or any VPS) with Docker and the Compose plugin installed, reachable over SSH, behind a reverse proxy (nginx/Apache/Caddy) that terminates TLS and forwards to the container. GitHub Actions connects with a dedicated deploy key — no manual server access is needed for day-to-day deploys.
The app itself runs in a single container (Dockerfile, gunicorn + WhiteNoise for static files); PostgreSQL is not containerized — it's expected to already be reachable from the server (e.g. a managed database, or Postgres installed directly on the host).
- Install Docker and the Docker Compose plugin on the server.
- Clone this repository on the server.
- Provision a PostgreSQL database and user for the app (e.g.
createdb coderr/createuser coderron Uberspace, or via your provider's dashboard). - Create a
.envfile in the repository root on the server (copy from.env.example) with production values — at minimumDEBUG=False, a freshly generatedSECRET_KEY,ALLOWED_HOSTS=coderr.marc-schaar.com,CSRF_TRUSTED_ORIGINS=https://coderr.marc-schaar.com, and theDB_*credentials for the database from step 3. This file is created once by hand and is never touched by the deploy workflow —docker composereads it viaenv_file. - Run
docker compose -f docker-compose.prod.yml up -d --buildonce by hand to build the image, create the schema (via the entrypoint'smigrate) and start the container. - Point your reverse proxy at
127.0.0.1:8002(the portdocker-compose.prod.ymlbinds on the host -8000was already taken by another app on this server) and configure TLS forcoderr.marc-schaar.com. - Generate a dedicated SSH keypair for deployments and add the public key to the server user's
~/.ssh/authorized_keys. The deploy user needs permission to rundocker/docker compose(e.g. membership in thedockergroup).
Add the following as Repository Secrets (Settings → Secrets and variables → Actions):
| Secret | Description |
|---|---|
SSH_HOST |
Server hostname or IP |
SSH_USER |
SSH username on the server |
SSH_PORT |
SSH port (optional, defaults to 22) |
SSH_PRIVATE_KEY |
Private key of the dedicated deploy keypair (never the personal key) |
DEPLOY_PATH |
Absolute path to the cloned repository on the server |
Once these secrets are set, every push to master deploys automatically.
coderr_backend/
├── core/ # Django project settings, URL routing, WSGI/ASGI
├── app_accounts/ # Users, profiles, authentication
├── app_offers/ # Offers and offer details
├── app_orders/ # Orders
├── app_reviews/ # Reviews
├── app_platform/ # Platform-wide info endpoints
├── .github/workflows/ # CI (tests.yml) and CD (deploy.yml) pipelines
├── .env.example # Documented template for environment variables
├── Dockerfile # Production container image (gunicorn + WhiteNoise)
├── docker-compose.prod.yml # Compose file used by the deploy pipeline
├── entrypoint.sh # Container startup: migrate, then run gunicorn
└── requirements.txt # Python dependencies
Each app follows the same internal layout: models.py, admin.py, api/ (serializers, views, urls, permissions) and test_app/ for tests.
SECRET_KEY,DEBUGandALLOWED_HOSTSare environment-based (see Configuration); nothing sensitive is hardcoded incore/settings.py.- Filtering and searching are available for offers and reviews via
django-filter.
Coderr is the backend for a freelance developer platform; the frontend is a separate project and connects to this API. This project was built as part of the Developer Akademie Backend course, providing hands-on experience with:
- RESTful APIs with Django & Django REST Framework
- User authentication and permissions
- Project, task and profile management
- File uploads and media handling
- Data validation, error handling and serialization
- A CI/CD pipeline with automated testing and deployment
Marc Schaar 📧 kontakt@marc-schaar.com · 🌐 marc-schaar.com · 💻 GitHub