Skip to content

AlanValdevenito/Planner-Web-Application

Repository files navigation

Planner Web Application

A Web Application built with FastAPI.

In this application, registered users will be able to create, update, and delete events.

About

This project is based on the book "Building Python Web APIs with FastAPI".

Chapters 5, 6, 7, 8, and 9 from the book have been implemented, which cover the development of a Planner Application.

The book covers essential topics for building web APIs with FastAPI, including:

  • FastAPI Basics: Setting up FastAPI applications, routing, and request handling
  • Pydantic Models: Data validation and serialization using Pydantic schemas
  • Database Integration: Connecting to MongoDB and performing CRUD operations
  • Authentication & Authorization: Implementing JWT-based authentication, password hashing, and route protection
  • Testing: Writing comprehensive tests with pytest and pytest-asyncio
  • Dockerization: Containerizing applications with Docker and Docker Compose
  • API Documentation: Automatic OpenAPI documentation generation with Swagger and ReDoc

Prerequisites

Before setting up and running this project, ensure the following tools and dependencies are installed on your system.

The instructions below are based on Ubuntu/Debian-based distributions.

Once all prerequisites are installed, you can proceed to the next section.

1. Install Git

Git is required to clone the repository.

# Install
$ sudo apt install git

# Verify installation
$ git --version

2. Install Python

# Verify installation
$ python3 --version

3. Install venv (Virtual Environment)

# Install
$ sudo apt install python3.12-venv

4. Ensure pip is available inside the virtual environment

# Create virtual environment
$ python3 -m venv .venv

# Activate virtual environment
source .venv/bin/activate

# Install/upgrade pip
(venv) $ python -m ensurepip --upgrade

# Verify pip
(venv) $ python -m pip --version
(venv) $ python -m pip

5. Install Docker

Docker is required to run the application using containers.

# Update system packages
$ sudo apt update
$ sudo apt upgrade -y

# Install required dependencies
$ sudo apt install -y \
  ca-certificates \
  curl \
  gnupg \
  lsb-release

# Add Docker’s official GPG key
$ sudo mkdir -p /etc/apt/keyrings

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Add Docker repository
$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
$ sudo apt update
$ sudo apt install -y docker-ce docker-ce-cli containerd.io

#Verify installation
$ docker --version
$ docker run hello-world

6. Install curl

Curl is useful for testing API endpoints from the terminal.

# Install
$ sudo apt update
$ sudo apt install curl

# Verify
$ curl --version

Setup, installation and start application

Virtual environment

1. Setup

Create the virtual environment:

# Create virtual environment
$ python3 -m venv .venv

Verify Docker is installed and running (required for MongoDB):

# Check Docker version
$ docker --version

# Check Docker service status
$ sudo systemctl status docker

# Start Docker if needed
$ sudo systemctl enable --now docker

2. Installation

Activate the virtual environment:

# Activate virtual environment in Linux
$ source .venv/bin/activate

Install project dependencies:

# Install dependencies
(venv) $ python3 -m pip install -r requirements.txt

Create MongoDB container:

# Run MongoDB container
$ sudo docker run -d --name mongodb -p 27017:27017 -v mongodb_data:/data/db mongo:7.0

Configure environment variables by creating a .env file in the project root:

DATABASE_URL=mongodb://localhost:27017/planner

3. Start

Start MongoDB container (if not already running):

# Start MongoDB container
$ sudo docker start mongodb

# Check if the container is running
$ sudo docker ps

Start the API:

# Run API
(venv) $ python main.py

Useful commands

Virtual environment:

# Deactivate virtual environment
(venv) $ deactivate

# Reactivate when needed
$ source .venv/bin/activate

MongoDB container management:

# Check container status (including stopped containers)
$ sudo docker ps -a

# Stop MongoDB container
$ sudo docker stop mongodb

# Restart MongoDB container
$ sudo docker restart mongodb

# View MongoDB logs
$ sudo docker logs mongodb

# Remove MongoDB container (if you want to recreate it)
$ sudo docker rm mongodb

Clean up Docker containers:

# Stop all running containers
$ sudo docker stop $(sudo docker ps -q)

# Remove specific container by name or ID
$ sudo docker rm container_name_or_id

# Remove multiple containers
$ sudo docker rm mongodb flamboyant_edison reverent_payne

# Remove all stopped containers
$ sudo docker container prune

# Remove all containers (stopped and running)
$ sudo docker rm -f $(sudo docker ps -aq)

# Remove all stopped containers, unused networks, and dangling images
$ sudo docker system prune

# Remove everything (containers, images, volumes, networks)
$ sudo docker system prune -a --volumes

Access MongoDB shell (inside Docker):

Open the MongoDB shell inside the container:

$ sudo docker exec -it mongodb mongosh

Example commands to view your data:

# Select the database
test> use planner

# List collections
planner> show collections

# List users
planner> db.users.find({})

# Delete collection
planner> db.users.drop()

# Empty without dropping
planner> db.users.deleteMany({})

# Exit the shell
planner> quit

Docker and Docker compose

The application includes a Dockerfile and docker-compose.yml for containerized deployment. This approach packages the application with all its dependencies, ensuring consistency across different environments.

1. Setup

Check your Docker version to determine which Docker Compose command to use:

# Check Docker version
$ docker --version

# Check Docker service status
$ sudo systemctl status docker

# Start Docker if needed
$ sudo systemctl enable --now docker

Docker Compose versions:

  • Modern Docker versions (20.10+) include Docker Compose V2 as a built-in plugin (use docker compose with a space)
  • Older Docker versions require separate installation with sudo apt install docker-compose (use docker-compose with a hyphen)

Important: All commands in this README use docker compose (with a space), which is the modern Docker Compose V2 syntax available in Docker 20.10+. If you have an older Docker version, you'll need to install docker-compose separately and replace docker compose with docker-compose (with a hyphen) in all commands below.

2. Installation

Configure environment variables by creating a .env file in the project root:

DATABASE_URL=mongodb://database:27017/planner

Build and prepare containers:

$ sudo docker compose build

3. Start

Start all services (API + MongoDB):

$ sudo docker compose up -d

Useful commands

Docker compose commands:

# Stop all services
$ sudo docker compose down

# Stop and remove volumes
$ sudo docker compose down -v

# View logs
$ sudo docker compose logs

# View logs for specific service
$ sudo docker compose logs api

# Follow logs in real-time
$ sudo docker compose logs -f

# Follow logs in real-time for specific service
$ sudo docker compose logs api -f

# Restart services
$ sudo docker compose restart

# View running containers
$ sudo docker compose ps

# Rebuild and start services
$ sudo docker compose up -d --build

Run tests with Docker Compose:

# Run all tests
$ sudo docker compose exec api pytest

# Run tests with verbose output
$ sudo docker compose exec api pytest -v

# Run a specific test file
$ sudo docker compose exec api pytest tests/test_events.py

# Run a specific test function
$ sudo docker compose exec api pytest tests/test_events.py::test_create_event

# Run tests with coverage
$ sudo docker compose exec api coverage run -m pytest

# Display coverage report
$ sudo docker compose exec api coverage report

Run linter with Docker Compose:

# Run Ruff linter
$ sudo docker compose exec api ruff check .

# Run Ruff formatter check
$ sudo docker compose exec api ruff format --check .

# Run Ruff formatter (apply formatting)
$ sudo docker compose exec api ruff format .

Clean up Docker Compose resources:

# Remove all stopped containers, networks, and images created by docker compose
$ sudo docker compose down --rmi all

# Remove all stopped containers, networks, images, and volumes
$ sudo docker compose down --rmi all -v

# Remove only specific service containers
$ sudo docker compose rm api

# Force remove containers without confirmation
$ sudo docker compose rm -f

API Documentation

FastAPI automatically generates interactive API documentation using Swagger and ReDoc.

Swagger

Access the interactive API documentation where you can test endpoints directly in your browser:

http://localhost:8080/docs

ReDoc

Access an alternative documentation interface with a clean, three-panel design:

http://localhost:8080/redoc

API Endpoints

Method Endpoint Description
GET /event/ List all events
GET /event/{event_id} Retrieve a specific event by ID
POST /event/new Create a new event
PUT /event/edit/{event_id} Update a specific event by ID
DELETE /event/{event_id} Delete a specific event by ID
DELETE /event/ Delete all events
POST /user/signup Register a new user
POST /user/signin Sign in a user

Request and response examples

Sign up

Request:

curl -X 'POST' \
	'http://0.0.0.0:8080/user/signup' \
	-H 'accept: application/json' \
	-H 'Content-Type: application/json' \
	-d '{
	"email": "[email protected]",
	"password": "exemplary",
	"events": []
}'

Response:

{
	"message": "User created successfully"
}

Sign in

Request:

curl -X 'POST' \
	'http://0.0.0.0:8080/user/signin' \
	-H 'accept: application/json' \
	-H 'Content-Type: application/x-www-form-urlencoded' \
	-d 'grant_type=&username=Example%40example.com&password=exemplary&scope=&client_id=&client_secret='

Response:

{
	"access_token": "<JWT_TOKEN>",
	"token_type": "Bearer"
}

List events

Request:

curl -X 'GET' \
	'http://0.0.0.0:8080/event/' \
	-H 'accept: application/json'

Response:

[]

Get event by ID

Request:

curl -X 'GET' \
	'http://0.0.0.0:8080/event/698a1381a18d400a2e8d5f2e' \
	-H 'accept: application/json'

Response:

{
	"id": "698a1381a18d400a2e8d5f2e",
	"title": "FastAPI Book Launch CLI",
	"image": "https://linktomyimage.com/image.png",
	"description": "We will be discussing the contents of the FastAPI book in this event. Ensure to come with your own copy to win gifts!",
	"tags": [
		"python",
		"fastapi",
		"book",
		"launch"
	],
	"location": "Google Meet"
}

Create event

Request:

curl -X 'POST' \
	'http://0.0.0.0:8080/event/new' \
	-H 'accept: application/json' \
	-H 'Authorization: Bearer <JWT_TOKEN>' \
	-H 'Content-Type: application/json' \
	-d '{
	"title": "FastAPI Book Launch CLI",
	"image": "https://linktomyimage.com/image.png",
	"description": "We will be discussing the contents of the FastAPI book in this event. Ensure to come with your own copy to win gifts!",
	"tags": [
		"python",
		"fastapi",
		"book",
		"launch"
	],
	"location": "Google Meet"
}'

Response:

{
	"message": "Event created successfully",
	"id": "698b48d7b891862af3a15ac6"
}

Delete event by ID

Request:

curl -X 'DELETE' \
	'http://0.0.0.0:8080/event/698b48d7b891862af3a15ac6' \
	-H 'accept: application/json' \
	-H 'Authorization: Bearer <JWT_TOKEN>'

Response:

{
	"message": "Event deleted successfully"
}

Delete all events

Request:

curl -X 'DELETE' \
	'http://0.0.0.0:8080/event/' \
	-H 'accept: application/json'

Response:

{
	"message": "All events deleted successfully"
}

Update event

Request:

curl -X 'PUT' \
	'http://0.0.0.0:8080/event/edit/698b48d7b891862af3a15ac6' \
	-H 'accept: application/json' \
	-H 'Authorization: Bearer <JWT_TOKEN>' \
	-H 'Content-Type: application/json' \
	-d '{
	"title": "Event update"
}'

Response:

{
	"id": "698b48d7b891862af3a15ac6",
	"title": "Event update"
}

Testing

This project uses pytest and pytest-asyncio for testing. All tests are asynchronous and interact with a test database that is cleaned after each test execution.

Testing Libraries

  • pytest: A powerful testing framework that simplifies test writing with fixtures and decorators
  • pytest-asyncio: An extension that enables pytest to run async test functions and fixtures
  • httpx: Asynchronous HTTP client used to make requests to the FastAPI application during tests

Test decorators and fixtures

@pytest.mark.asyncio

This decorator marks a test function as asynchronous. It allows pytest to run the async function in the event loop.

@pytest.fixture

A fixture is a reusable function that sets up test data or resources before a test runs. It can also clean up after the test completes.

Fixture scopes

Fixtures have different scopes that determine how long they live:

  1. scope="function" (default): Creates a new fixture instance for each test function. Use this when you need fresh data for each test. Database cleanup happens after each test.
  2. scope="module": Creates one fixture instance for all tests in a module. Useful for expensive setups that multiple tests can share (like access tokens).
  3. scope="session": Creates one fixture instance for the entire test session. Use this for resources needed across all tests, like the event loop.

Test configuration

Tests are configured in pytest.ini:

[pytest]
asyncio_mode = auto

This tells pytest-asyncio to automatically detect and run async fixtures and tests.

Running Tests with Docker

If you are using Docker Compose, you can run the tests inside the api container:

# Run all tests
$ sudo docker compose exec api pytest

# Run tests with verbose output
$ sudo docker compose exec api pytest -v

# Run a specific test file
$ sudo docker compose exec api pytest tests/test_events.py

# Run a specific test function
$ sudo docker compose exec api pytest tests/test_events.py::test_create_event

Linter

This project uses Ruff as its linter and code formatter. Ruff is an extremely fast Python linter and formatter written in Rust, designed as a drop-in replacement for tools like Flake8, isort, and Black. It is included in the project dependencies.

Running Linter with Docker

You can run the Ruff linter and formatter inside the api container:

# Run Ruff linter
$ sudo docker compose exec api ruff check .

# Run Ruff formatter check
$ sudo docker compose exec api ruff format --check .

# Run Ruff formatter (apply formatting)
$ sudo docker compose exec api ruff format .

Coverage Report

Use coverage.py to measure code coverage and identify untested parts of your codebase.

With Docker

# Run tests with coverage
$ sudo docker compose exec api coverage run -m pytest

# Display coverage report in terminal
$ sudo docker compose exec api coverage report

# Generate HTML coverage report
$ sudo docker compose exec api coverage html

# Clear coverage data
$ sudo docker compose exec api coverage erase

The HTML report provides detailed line-by-line coverage information for each file.

Project structure

.
├── main.py                 # Application entry point
├── requirements.txt        # Python dependencies
├── pytest.ini              # Pytest configuration
├── Dockerfile              # Docker image configuration
├── docker-compose.yml      # Multi-container Docker configuration
├── README.md               # Project documentation
├── auth/                   # Authentication module
│   ├── __init__.py
│   ├── authenticate.py     # Authentication dependency for route protection
│   ├── hash_password.py    # Password hashing and verification functions
│   └── jwt_handler.py      # JWT encoding and decoding functions
├── database/               # Database configuration and connections
│   ├── __init__.py
│   └── connection.py       # MongoDB connection setup
├── models/                 # Data models (Pydantic schemas)
│   ├── __init__.py
│   ├── events.py           # Event model definitions
│   └── users.py            # User model definitions
├── routes/                 # API route handlers
│   ├── __init__.py
│   ├── events.py           # Event CRUD endpoints
│   └── users.py            # User authentication endpoints (signup/signin)
└── tests/                  # Test suite
    ├── __init__.py
    ├── conftest.py         # Pytest fixtures and configuration
    ├── test_events.py      # Event endpoint tests
    └── test_users.py       # User endpoint tests

About

Planner Web Application built with FastAPI as a learning project to explore the fundamentals of modern API development in Python

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors