A comprehensive guide and implementation to help developers learn how to create RESTful APIs using Python, Flask, Docker, and MongoDB. It demonstrates best practices for building scalable and efficient APIs, leveraging Python's capabilities alongside Docker for containerization. The repository serves as an educational resource for both beginners and experienced developers looking to refine their skills in REST API development.
Reference article: https://www.dvt.co.za/news-insights/insights/item/355-restful-web-services-using-python-flask-docker-and-mongodb [Specific to commit sha 87722d939eadaca906fade165829eddb59f906d1] The project has since been updated to use JWT instead of basic auth
web/ Python application source (Flask app + Dockerfile)
web/requirements.txt Pinned runtime and dev dependencies
web/tests/ Unit tests for the Flask application
tests/ Integration / config-level tests
scripts/lint.sh Local lint script (mirrors CI exactly)
docker-compose.yml Service definitions: app + MongoDB
.env.example Environment variable template
cp .env.example .envEdit .env and set a strong JWT_SECRET before starting the application.
sudo docker-compose build
sudo docker-compose upOpen http://localhost:5000/hello in your browser or run:
curl http://localhost:5000/helloExpected response: "Hello World!"
| Variable | Default | Description |
|---|---|---|
| MONGO_URI | mongodb://my_db:27017/ | MongoDB connection string. Override for remote or authenticated instances. |
| JWT_SECRET | (required) | Secret key for signing and verifying JWT tokens. Use a long random string. |
Never hardcode or commit JWT_SECRET.
The API uses JWT (JSON Web Token) bearer authentication.
curl -X POST http://localhost:5000/register \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "yourpassword"}'curl -X POST http://localhost:5000/login \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "yourpassword"}'Response (200 OK):
{"status": 200, "token": "<signed-jwt>"}On invalid credentials the endpoint returns 401.
Pass the token in the Authorization header on every call to /retrieve and /save:
# Retrieve messages
curl -X POST http://localhost:5000/retrieve \
-H "Authorization: Bearer <token>"
# Save a message
curl -X POST http://localhost:5000/save \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"message": "Hello!"}'Missing, expired, or tampered tokens return 401 Unauthorized.
Add Content-Type: application/json to your request headers.
If you prefer not to set the content-type header manually, the endpoints use
request.get_json(force=True) which will parse the body as JSON regardless.
See #1 for context.
Install dependencies and run the full test suite from the project root:
pip install -r web/requirements.txt
pytest -vThis runs both web/tests/ (unit tests) and tests/ (config and API-contract tests).
The project uses Ruff for linting and formatting (replaces flake8 + isort + black in a single fast tool).
# Check only
./scripts/lint.sh
# Auto-fix then check
./scripts/lint.sh --fixruff check web/ tests/ # lint
ruff format --check web/ tests/ # format check
ruff format web/ tests/ # apply formattingRuff is included in web/requirements.txt so no separate install is needed
once the project dependencies are installed.
Every push and pull request runs the GitHub Actions CI pipeline:
lint → test
- lint —
ruff check+ruff format --checkacrossweb/andtests/ - test —
pytest -v(only runs if lint passes)
The pipeline is defined in .github/workflows/ci.yml.
All runtime and development dependencies are pinned in web/requirements.txt
to ensure reproducible builds across local, CI, and Docker environments.
| Package | Version | Role |
|---|---|---|
| Flask | 3.1.3 | Web framework |
| Werkzeug | >=3.0.0 | WSGI utilities |
| flask-restful | 0.3.10 | REST resource helpers |
| pymongo | 4.7.2 | MongoDB driver |
| bcrypt | 4.1.3 | Password hashing |
| PyJWT | >=2.8.0 | JWT authentication |
| pytest | 9.0.3 | Test runner (dev) |
| pytest-cov | >=4.1 | Coverage reports (dev) |
| ruff | >=0.5.0 | Linting and formatting (dev) |
- Update the version in
web/requirements.txt. - Reinstall:
pip install -r web/requirements.txt - Run the test suite:
pytest -v - Run the linter:
./scripts/lint.sh - If all checks pass, commit the updated
requirements.txt.
Contributions are welcome. Please read CONTRIBUTING.md for the full workflow, including how to pick up an issue, branch naming conventions, local validation steps, and the pull request process.