Compare commits
21 Commits
88ac98c62f
...
docker
| Author | SHA1 | Date | |
|---|---|---|---|
| 42f8df2589 | |||
|
|
90f5451ff8 | ||
|
|
2e229ceadd | ||
|
|
32615bf615 | ||
|
|
7de5efab3f | ||
|
|
6fbd717868 | ||
|
|
6be588aa58 | ||
|
|
01a3efd424 | ||
|
|
38943392c2 | ||
|
|
78f6dcb356 | ||
|
|
ad45fd4ee2 | ||
|
|
a2aed1117f | ||
|
|
6cf2b3c3ef | ||
|
|
a5812354c2 | ||
|
|
aa3957945c | ||
|
|
729e73bae8 | ||
| d91d177fa9 | |||
| fa455aaa33 | |||
| b8e9dc2ae7 | |||
| 2ee6a1aba3 | |||
| 7067c77714 |
274
Docs/Libs/Docker/01-Setup.md
Normal file
274
Docs/Libs/Docker/01-Setup.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# Docker SDK for Python – Setup and First Steps
|
||||
|
||||
This document introduces the **Docker SDK for Python (`docker` library)** and explains how to connect to the Docker Engine, inspect its status, and authenticate with a registry. The goal is not just to show code, but to clearly explain **what each part does, why it exists, and when you would use it**.
|
||||
|
||||
This guide assumes you already understand basic Docker concepts (images, containers, daemon) and are approaching this from a DevOps perspective.
|
||||
|
||||
---
|
||||
|
||||
## 1. Installing the Docker SDK for Python
|
||||
|
||||
Before Python can communicate with Docker, we need to install the official SDK.
|
||||
|
||||
```bash
|
||||
pip install docker
|
||||
```
|
||||
|
||||
### What this does
|
||||
|
||||
* Installs the **docker** Python package (often called *docker-py*).
|
||||
* This package acts as a **client wrapper** around Docker’s REST API.
|
||||
* All interactions ultimately talk to the Docker daemon (`dockerd`) over a socket or TCP connection.
|
||||
|
||||
Important note:
|
||||
|
||||
* Installing the library alone is not enough.
|
||||
* Docker **must already be installed and running** on the system.
|
||||
|
||||
---
|
||||
|
||||
## 2. Connecting to Docker Using Environment Configuration
|
||||
|
||||
The simplest and most common way to create a Docker client is by using environment-based configuration.
|
||||
|
||||
```python
|
||||
import docker
|
||||
|
||||
client = docker.from_env()
|
||||
version = client.version()
|
||||
ping_docker = client.ping()
|
||||
|
||||
print(version, ping_docker)
|
||||
```
|
||||
|
||||
### Step-by-step explanation
|
||||
|
||||
#### `import docker`
|
||||
|
||||
* Imports the Docker SDK.
|
||||
* This module exposes high-level objects for interacting with Docker resources.
|
||||
|
||||
#### `docker.from_env()`
|
||||
|
||||
* Automatically creates a `DockerClient` instance.
|
||||
* Reads Docker connection details from environment variables such as:
|
||||
|
||||
* `DOCKER_HOST`
|
||||
* `DOCKER_TLS_VERIFY`
|
||||
* `DOCKER_CERT_PATH`
|
||||
* On Linux, this usually resolves to:
|
||||
|
||||
* `unix:///var/run/docker.sock`
|
||||
|
||||
This is why `from_env()` is preferred:
|
||||
|
||||
* Works across Linux, macOS, Windows, and CI systems
|
||||
* Requires no hardcoded connection strings
|
||||
|
||||
#### `client.version()`
|
||||
|
||||
* Calls the Docker Engine `/version` API endpoint.
|
||||
* Returns detailed metadata such as:
|
||||
|
||||
* Docker Engine version
|
||||
* API version
|
||||
* Go version
|
||||
* OS and architecture
|
||||
|
||||
This is commonly used for:
|
||||
|
||||
* Debugging
|
||||
* Compatibility checks
|
||||
* Logging runtime environment details
|
||||
|
||||
#### `client.ping()`
|
||||
|
||||
* Sends a lightweight request to the Docker daemon.
|
||||
* Returns `True` if Docker is reachable and responsive.
|
||||
|
||||
This is a **health check**, often used in:
|
||||
|
||||
* Startup validation
|
||||
* Monitoring scripts
|
||||
* CI/CD pipelines
|
||||
|
||||
---
|
||||
|
||||
## 3. What Does “from_env” Actually Mean?
|
||||
|
||||
Using `from_env()` tells the SDK:
|
||||
|
||||
> “Figure out how to connect to Docker using the same configuration the Docker CLI uses.”
|
||||
|
||||
Behind the scenes, it:
|
||||
|
||||
* Detects whether Docker is local or remote
|
||||
* Determines socket vs TCP
|
||||
* Applies TLS settings if required
|
||||
|
||||
### When should you use it?
|
||||
|
||||
* Local development
|
||||
* Kubernetes nodes
|
||||
* CI runners
|
||||
* Most production automation
|
||||
|
||||
### When not to use it?
|
||||
|
||||
* When you need explicit control over connection parameters
|
||||
* When connecting to a **remote Docker daemon** with custom networking
|
||||
|
||||
---
|
||||
|
||||
## 4. Creating a Docker Client Explicitly
|
||||
|
||||
Instead of relying on environment detection, you can create a client manually.
|
||||
|
||||
```python
|
||||
import docker
|
||||
|
||||
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||
version = client.version()
|
||||
ping_docker = client.ping()
|
||||
|
||||
print(version, ping_docker)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
#### `docker.DockerClient(...)`
|
||||
|
||||
* Directly instantiates a Docker client object.
|
||||
* Requires you to specify how to reach the Docker daemon.
|
||||
|
||||
#### `base_url='unix://var/run/docker.sock'`
|
||||
|
||||
* Points to the Unix socket used by Docker on Linux systems.
|
||||
* This socket is owned by root and the `docker` group.
|
||||
|
||||
Important implications:
|
||||
|
||||
* Your Python process must have permission to access the socket
|
||||
* Usually means running as root or a user in the `docker` group
|
||||
|
||||
### When this approach is useful
|
||||
|
||||
* Controlled environments
|
||||
* Educational examples
|
||||
* Explicit infrastructure scripts
|
||||
|
||||
### Why it is less common
|
||||
|
||||
* Not portable across OSes
|
||||
* Hardcodes infrastructure assumptions
|
||||
|
||||
---
|
||||
|
||||
## 5. Inspecting Docker and Authenticating
|
||||
|
||||
Once connected, the client can retrieve detailed system information and authenticate with registries.
|
||||
|
||||
```python
|
||||
import docker
|
||||
|
||||
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||
version = client.version()
|
||||
ping_docker = client.ping()
|
||||
information = client.info()
|
||||
|
||||
client.login(
|
||||
username='user',
|
||||
password='password',
|
||||
registry='registry_link'
|
||||
)
|
||||
|
||||
print(information, ping_docker)
|
||||
```
|
||||
|
||||
### `client.info()`
|
||||
|
||||
* Calls the Docker `/info` endpoint.
|
||||
* Returns a comprehensive snapshot of the Docker host, including:
|
||||
|
||||
* Number of containers (running/stopped)
|
||||
* Number of images
|
||||
* Storage driver
|
||||
* Cgroup and kernel features
|
||||
* Security options (AppArmor, seccomp)
|
||||
|
||||
This is extremely valuable for:
|
||||
|
||||
* Capacity planning
|
||||
* Debugging runtime issues
|
||||
* Auditing host configuration
|
||||
|
||||
### `client.login(...)`
|
||||
|
||||
* Authenticates Docker with a container registry.
|
||||
* Stores credentials in Docker’s credential store.
|
||||
|
||||
Parameters:
|
||||
|
||||
* `username`: registry account username
|
||||
* `password`: registry password or access token
|
||||
* `registry`: registry URL (e.g. Docker Hub or private registry)
|
||||
|
||||
Common use cases:
|
||||
|
||||
* Pulling private images
|
||||
* Pushing images in CI/CD pipelines
|
||||
* Automating registry interactions
|
||||
|
||||
Security note:
|
||||
|
||||
* Avoid hardcoding credentials in source code
|
||||
* Prefer environment variables or secret managers
|
||||
|
||||
---
|
||||
|
||||
## 6. High-Level vs Low-Level API (Important Concept)
|
||||
|
||||
What you are using here is the **high-level Docker client**.
|
||||
|
||||
Characteristics:
|
||||
|
||||
* Pythonic object model
|
||||
* Resource-oriented (containers, images, networks)
|
||||
* Easier to read and maintain
|
||||
|
||||
The SDK also exposes a **low-level API**:
|
||||
|
||||
* Direct access to Docker REST endpoints
|
||||
* More control, less abstraction
|
||||
|
||||
As a DevOps engineer, you will typically:
|
||||
|
||||
* Use high-level API for automation
|
||||
* Drop to low-level API for advanced edge cases
|
||||
|
||||
---
|
||||
|
||||
## 7. Summary
|
||||
|
||||
In this setup phase, you learned how to:
|
||||
|
||||
* Install the Docker SDK for Python
|
||||
* Connect to Docker using environment-based configuration
|
||||
* Explicitly define Docker connection settings
|
||||
* Verify Docker availability
|
||||
* Retrieve system-level Docker information
|
||||
* Authenticate with container registries
|
||||
|
||||
This foundation is critical before moving on to:
|
||||
|
||||
* Managing containers
|
||||
* Building images
|
||||
* Working with volumes and networks
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
Official Docker SDK for Python documentation:
|
||||
|
||||
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|
||||
360
Docs/Libs/Docker/02-Images.md
Normal file
360
Docs/Libs/Docker/02-Images.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# Docker SDK for Python – Working with Images
|
||||
|
||||
This document explains how to **manage Docker images** using the Docker SDK for Python. Instead of treating the SDK as a set of function calls, we’ll approach images the same way Docker itself does: as immutable artifacts that are pulled, built, tagged, pushed, inspected, and eventually cleaned up.
|
||||
|
||||
All examples assume:
|
||||
|
||||
* Docker is installed and running
|
||||
* The Python process has access to the Docker socket
|
||||
|
||||
---
|
||||
|
||||
## 1. Creating the Docker Client
|
||||
|
||||
```python
|
||||
import docker
|
||||
|
||||
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||
ping_docker = client.ping()
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* `DockerClient` establishes a connection to the Docker daemon.
|
||||
* `ping()` verifies that Docker is reachable before doing any real work.
|
||||
|
||||
This is a common pattern in automation:
|
||||
|
||||
* Fail fast if Docker is unavailable
|
||||
* Avoid partial execution later in the script
|
||||
|
||||
---
|
||||
|
||||
## 2. Pulling Images from a Registry
|
||||
|
||||
```python
|
||||
def pull_image(name_image, tag_image):
|
||||
image = client.images.pull(name_image, tag=tag_image)
|
||||
print(image)
|
||||
```
|
||||
|
||||
### What this does
|
||||
|
||||
* Downloads an image from a registry (Docker Hub or private registry).
|
||||
* If the image already exists locally, Docker may reuse layers.
|
||||
|
||||
Parameters:
|
||||
|
||||
* `name_image`: repository name (e.g. `alpine`, `nginx`, `myrepo/app`)
|
||||
* `tag_image`: specific version or variant (e.g. `3.20`, `latest`)
|
||||
|
||||
Returned value:
|
||||
|
||||
* An `Image` object representing the pulled image
|
||||
|
||||
Why this matters:
|
||||
|
||||
* Pulling explicitly avoids relying on implicit image downloads
|
||||
* Makes automation predictable and repeatable
|
||||
|
||||
---
|
||||
|
||||
## 3. Building Images from a Dockerfile
|
||||
|
||||
### Basic build
|
||||
|
||||
```python
|
||||
def build_image():
|
||||
image, logs = client.images.build(
|
||||
path=".",
|
||||
tag="myapp:1.0"
|
||||
)
|
||||
|
||||
for log in logs:
|
||||
if "stream" in log:
|
||||
print(log["stream"].strip())
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* `path="."` tells Docker to use the current directory as the build context.
|
||||
* Docker automatically looks for a file named `Dockerfile`.
|
||||
* `tag="myapp:1.0"` assigns a name and version to the resulting image.
|
||||
|
||||
The build process returns:
|
||||
|
||||
* `image`: the final built image object
|
||||
* `logs`: a stream of build output messages
|
||||
|
||||
Printing build logs is important because:
|
||||
|
||||
* Docker build failures are only visible in logs
|
||||
* CI pipelines rely on this output for debugging
|
||||
|
||||
---
|
||||
|
||||
## 4. Advanced Build with Custom Dockerfile and Build Arguments
|
||||
|
||||
```python
|
||||
def build_image_2():
|
||||
image, logs = client.images.build(
|
||||
path=".",
|
||||
dockerfile="Dockerfile.prod",
|
||||
tag="myapp:prod",
|
||||
buildargs={
|
||||
"APP_ENV": "production",
|
||||
"VERSION": "1.0.0"
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
This version adds more control:
|
||||
|
||||
* `dockerfile="Dockerfile.prod"`
|
||||
|
||||
* Allows multiple Dockerfiles per project
|
||||
* Common for dev vs prod builds
|
||||
|
||||
* `buildargs`
|
||||
|
||||
* Passed to `ARG` instructions inside the Dockerfile
|
||||
* Enables parameterized builds without editing the Dockerfile
|
||||
|
||||
Typical DevOps use cases:
|
||||
|
||||
* Environment-specific builds
|
||||
* Injecting version numbers
|
||||
* Feature flags during build time
|
||||
|
||||
---
|
||||
|
||||
## 5. Tagging Images
|
||||
|
||||
```python
|
||||
def tag_image():
|
||||
image = client.images.get("myapp:1.0")
|
||||
image.tag("myrepo/myapp", tag="latest")
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Docker images are immutable, but tags are not.
|
||||
* This creates an additional reference to the same image ID.
|
||||
|
||||
Why tagging is important:
|
||||
|
||||
* One image can have multiple tags
|
||||
* Tags represent lifecycle stages (`1.0`, `prod`, `latest`)
|
||||
|
||||
This is how promotion pipelines work:
|
||||
|
||||
* Build once
|
||||
* Tag many times
|
||||
* Push selectively
|
||||
|
||||
---
|
||||
|
||||
## 6. Removing Images
|
||||
|
||||
```python
|
||||
def remove_image():
|
||||
client.images.remove("myapp:1.0", force=True)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Removes the image reference from the local Docker host.
|
||||
* `force=True` removes the image even if it is in use by stopped containers.
|
||||
|
||||
Use with care:
|
||||
|
||||
* Running containers still prevent deletion
|
||||
* Forced removal is destructive
|
||||
|
||||
---
|
||||
|
||||
## 7. Pushing Images to a Registry
|
||||
|
||||
```python
|
||||
def push_image():
|
||||
client.images.push(
|
||||
repository="myrepo/myapp",
|
||||
tag="latest"
|
||||
)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Uploads the image layers to a registry.
|
||||
* Requires prior authentication (`client.login`).
|
||||
|
||||
Important notes:
|
||||
|
||||
* Only new or changed layers are pushed
|
||||
* Tags determine what remote users pull
|
||||
|
||||
This step is usually automated in:
|
||||
|
||||
* CI/CD pipelines
|
||||
* Release workflows
|
||||
|
||||
---
|
||||
|
||||
## 8. Cleaning Up Unused Images
|
||||
|
||||
```python
|
||||
def prune_images():
|
||||
result = client.images.prune()
|
||||
print(result)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Removes dangling images (untagged and unused).
|
||||
* Helps reclaim disk space on build servers.
|
||||
|
||||
The result includes:
|
||||
|
||||
* Number of images removed
|
||||
* Amount of disk space freed
|
||||
|
||||
This is essential for:
|
||||
|
||||
* CI runners
|
||||
* Long-lived build machines
|
||||
|
||||
---
|
||||
|
||||
## 9. Inspecting Image Metadata
|
||||
|
||||
```python
|
||||
def inspect_image():
|
||||
alpine_image = client.images.get("alpine:3.20")
|
||||
|
||||
print(alpine_image.attrs)
|
||||
print(alpine_image.attrs["Id"])
|
||||
print(alpine_image.attrs["Size"])
|
||||
print(alpine_image.attrs["Config"]["Env"])
|
||||
print(alpine_image.attrs["Config"]["Cmd"])
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* `attrs` exposes the raw Docker image inspection data.
|
||||
* This is equivalent to `docker image inspect`.
|
||||
|
||||
Useful fields:
|
||||
|
||||
* `Id`: content-addressable image hash
|
||||
* `Size`: image size in bytes
|
||||
* `Config.Env`: environment variables baked into the image
|
||||
* `Config.Cmd`: default command
|
||||
|
||||
This information is often used for:
|
||||
|
||||
* Debugging unexpected behavior
|
||||
* Auditing images
|
||||
* Validating build output
|
||||
|
||||
---
|
||||
|
||||
## 10. Listing Local Images
|
||||
|
||||
```python
|
||||
def image_list():
|
||||
images = client.images.list()
|
||||
for item in images:
|
||||
print(item.id, item.tags)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Lists all images stored locally.
|
||||
* Each image may have multiple tags or none.
|
||||
|
||||
This mirrors:
|
||||
|
||||
* `docker images`
|
||||
|
||||
---
|
||||
|
||||
## 11. Ensuring an Image Exists
|
||||
|
||||
```python
|
||||
def ensure_image(name):
|
||||
try:
|
||||
client.images.get(name)
|
||||
print(f"{name} exists")
|
||||
except docker.errors.ImageNotFound:
|
||||
print(f"Pulling {name}")
|
||||
client.images.pull(name)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
This is a very common DevOps pattern:
|
||||
|
||||
* Check if the image exists locally
|
||||
* Pull it only if necessary
|
||||
|
||||
Benefits:
|
||||
|
||||
* Avoids unnecessary network calls
|
||||
* Makes scripts idempotent
|
||||
|
||||
---
|
||||
|
||||
## 12. Important Exceptions to Know
|
||||
|
||||
When working with images, you must handle failures explicitly.
|
||||
|
||||
### `docker.errors.ImageNotFound`
|
||||
|
||||
* Raised when an image does not exist locally
|
||||
* Common when calling `get()`
|
||||
|
||||
### `docker.errors.BuildError`
|
||||
|
||||
* Raised when an image build fails
|
||||
* Usually due to Dockerfile errors or missing files
|
||||
|
||||
### `docker.errors.APIError`
|
||||
|
||||
* Raised for general Docker API failures
|
||||
* Includes permission issues, daemon errors, and network problems
|
||||
|
||||
Catching these exceptions is critical for:
|
||||
|
||||
* Reliable automation
|
||||
* Meaningful error reporting
|
||||
* CI/CD stability
|
||||
|
||||
---
|
||||
|
||||
## 13. Summary
|
||||
|
||||
In this section, you learned how to:
|
||||
|
||||
* Pull images from registries
|
||||
* Build images using Dockerfiles
|
||||
* Use build arguments and multiple Dockerfiles
|
||||
* Tag and push images
|
||||
* Inspect and list images
|
||||
* Clean up unused images
|
||||
* Handle common Docker image errors
|
||||
|
||||
This image workflow is the backbone of:
|
||||
|
||||
* CI pipelines
|
||||
* Release automation
|
||||
* Platform engineering systems
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
Official Docker SDK for Python documentation:
|
||||
|
||||
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|
||||
284
Docs/Libs/Docker/03-Containers.md
Normal file
284
Docs/Libs/Docker/03-Containers.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Docker SDK for Python – Working with Containers
|
||||
|
||||
This document explains how to **manage Docker containers** using the Docker SDK for Python. Containers are the runtime unit of Docker, so this section focuses on lifecycle management: listing, creating, running, starting, stopping, restarting, pausing, and inspecting logs.
|
||||
|
||||
The goal is to understand **how container state changes**, how the Python SDK maps to Docker CLI behavior, and how these operations are typically used in real DevOps automation.
|
||||
|
||||
---
|
||||
|
||||
## 1. Creating the Docker Client
|
||||
|
||||
```python
|
||||
import docker
|
||||
import time
|
||||
|
||||
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||
ping_docker = client.ping()
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Establishes a connection to the Docker daemon via the Unix socket.
|
||||
* `ping()` is used as a sanity check before executing container operations.
|
||||
|
||||
In production-grade scripts, this check prevents:
|
||||
|
||||
* Silent failures
|
||||
* Partial execution when Docker is down
|
||||
|
||||
---
|
||||
|
||||
## 2. Listing Running Containers
|
||||
|
||||
```python
|
||||
def get_containers_list():
|
||||
containers = client.containers.list()
|
||||
|
||||
for c in containers:
|
||||
print(c.id, c.name, c.status)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* `containers.list()` returns **only running containers** by default.
|
||||
* Each returned object represents a live container instance.
|
||||
|
||||
Common attributes:
|
||||
|
||||
* `id`: unique container identifier
|
||||
* `name`: human-readable container name
|
||||
* `status`: runtime state (running)
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
* `docker ps`
|
||||
|
||||
---
|
||||
|
||||
## 3. Listing All Containers (Including Stopped)
|
||||
|
||||
```python
|
||||
def get_all_containers_list():
|
||||
containers = client.containers.list(all=True)
|
||||
|
||||
for c in containers:
|
||||
print(c.id, c.name, c.status)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* `all=True` includes stopped, exited, and created containers.
|
||||
* Useful for cleanup, auditing, and lifecycle reconciliation.
|
||||
|
||||
Equivalent CLI command:
|
||||
|
||||
* `docker ps -a`
|
||||
|
||||
---
|
||||
|
||||
## 4. Creating a Container (Without Starting It)
|
||||
|
||||
```python
|
||||
def create_container():
|
||||
container = client.containers.create(
|
||||
image="nginx:latest",
|
||||
name="my_nginx",
|
||||
ports={"80/tcp": 8080},
|
||||
detach=True
|
||||
)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
This creates a container in the **created** state.
|
||||
|
||||
Key parameters:
|
||||
|
||||
* `image`: base image for the container
|
||||
* `name`: explicit container name
|
||||
* `ports`: port mapping (container → host)
|
||||
* `detach=True`: container runs in background when started
|
||||
|
||||
Important distinction:
|
||||
|
||||
* `create()` does **not** start the container
|
||||
* This mirrors `docker create`
|
||||
|
||||
Use cases:
|
||||
|
||||
* Deferred startup
|
||||
* Multi-step initialization
|
||||
|
||||
---
|
||||
|
||||
## 5. Running a Container (Create + Start)
|
||||
|
||||
```python
|
||||
def run_container():
|
||||
container = client.containers.run(
|
||||
"nginx:latest",
|
||||
name="nginx_run",
|
||||
ports={"80/tcp": 8081},
|
||||
detach=True
|
||||
)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* `run()` is a convenience method.
|
||||
* Internally performs `create()` followed by `start()`.
|
||||
|
||||
Equivalent CLI command:
|
||||
|
||||
* `docker run -d -p 8081:80 nginx:latest`
|
||||
|
||||
This is the most common approach for:
|
||||
|
||||
* Short-lived workloads
|
||||
* Simple services
|
||||
|
||||
---
|
||||
|
||||
## 6. Managing Container Lifecycle
|
||||
|
||||
```python
|
||||
def start_container():
|
||||
container = client.containers.get("my_nginx")
|
||||
|
||||
container.start()
|
||||
time.sleep(1000)
|
||||
|
||||
container.restart()
|
||||
time.sleep(1000)
|
||||
|
||||
container.pause()
|
||||
container.unpause()
|
||||
time.sleep(1000)
|
||||
|
||||
container.stop(timeout=10)
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
This function demonstrates multiple lifecycle transitions.
|
||||
|
||||
#### `containers.get(name)`
|
||||
|
||||
* Retrieves an existing container by name or ID.
|
||||
* Raises an exception if the container does not exist.
|
||||
|
||||
#### `start()`
|
||||
|
||||
* Transitions container from `created` or `stopped` → `running`.
|
||||
|
||||
#### `restart()`
|
||||
|
||||
* Stops and immediately starts the container.
|
||||
* Often used after configuration changes.
|
||||
|
||||
#### `pause()` / `unpause()`
|
||||
|
||||
* Freezes container processes using cgroups.
|
||||
* Network and filesystem state remain intact.
|
||||
|
||||
#### `stop(timeout=10)`
|
||||
|
||||
* Sends SIGTERM, then SIGKILL after timeout.
|
||||
* Allows graceful shutdown.
|
||||
|
||||
The `sleep()` calls simulate:
|
||||
|
||||
* Long-running services
|
||||
* Observing container behavior between states
|
||||
|
||||
---
|
||||
|
||||
## 7. Reading Container Logs
|
||||
|
||||
```python
|
||||
def log_container():
|
||||
container = client.containers.get("my_nginx")
|
||||
logs = container.logs(tail=20)
|
||||
print(logs.decode())
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Retrieves logs from the container’s stdout/stderr.
|
||||
* `tail=20` limits output to the last 20 lines.
|
||||
|
||||
Important details:
|
||||
|
||||
* Logs are returned as bytes
|
||||
* Decoding is required for readable output
|
||||
|
||||
Equivalent CLI command:
|
||||
|
||||
* `docker logs --tail 20 my_nginx`
|
||||
|
||||
This is critical for:
|
||||
|
||||
* Debugging runtime issues
|
||||
* Health checks
|
||||
* Observability tooling
|
||||
|
||||
---
|
||||
|
||||
## 8. Conditional Execution Based on Docker Availability
|
||||
|
||||
```python
|
||||
if ping_docker:
|
||||
get_containers_list()
|
||||
```
|
||||
|
||||
### Explanation
|
||||
|
||||
* Ensures container operations only run if Docker is reachable.
|
||||
* Prevents unhandled API errors.
|
||||
|
||||
This pattern is common in:
|
||||
|
||||
* Entry-point scripts
|
||||
* Health probes
|
||||
* Automation jobs
|
||||
|
||||
---
|
||||
|
||||
## 9. Container States (Conceptual Model)
|
||||
|
||||
Understanding container states is essential:
|
||||
|
||||
* `created`: container exists but is not running
|
||||
* `running`: container processes are active
|
||||
* `paused`: execution is suspended
|
||||
* `exited`: container stopped normally
|
||||
* `dead`: container failed unexpectedly
|
||||
|
||||
Every method in this document transitions the container between these states.
|
||||
|
||||
---
|
||||
|
||||
## 10. Summary
|
||||
|
||||
In this section, you learned how to:
|
||||
|
||||
* List running and stopped containers
|
||||
* Create containers separately from starting them
|
||||
* Run containers in a single step
|
||||
* Control container lifecycle states
|
||||
* Read container logs
|
||||
* Safely execute operations based on Docker availability
|
||||
|
||||
This container-level control is the foundation for:
|
||||
|
||||
* Service orchestration
|
||||
* CI/CD job execution
|
||||
* Debugging and recovery automation
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
Official Docker SDK for Python documentation:
|
||||
|
||||
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|
||||
68
Docs/Libs/Docker/05-Networks.md
Normal file
68
Docs/Libs/Docker/05-Networks.md
Normal file
@@ -0,0 +1,68 @@
|
||||
```python
|
||||
import docker
|
||||
import time
|
||||
|
||||
docker_client = docker.DockerClient(base_url="unix://var/run/docker.sock")
|
||||
docker_client.ping()
|
||||
|
||||
print("All Networks:\n")
|
||||
|
||||
all_networks = docker_client.networks.list()
|
||||
|
||||
for network in all_networks:
|
||||
print(network.name, network.id)
|
||||
|
||||
print("\nNetworks Named host and bridge:\n")
|
||||
|
||||
system_networks = docker_client.networks.list(names=["host", "bridge"])
|
||||
|
||||
for network in system_networks:
|
||||
print(network.name, network.id)
|
||||
|
||||
print("\nNetwork With Custom ID:\n")
|
||||
|
||||
custom_id_networks = docker_client.networks.list(
|
||||
ids=["29c9e588bb8e0db6445f2a2278a1c2f42e39dc163c0a404f744dc4139fe47d21"]
|
||||
)
|
||||
|
||||
for network in custom_id_networks:
|
||||
print(network.name, network.id)
|
||||
|
||||
print("\nNetwork With Custom ID (Including Attributes):\n")
|
||||
|
||||
custom_id_networks = docker_client.networks.list(
|
||||
ids=["29c9e588bb8e0db6445f2a2278a1c2f42e39dc163c0a404f744dc4139fe47d21"]
|
||||
)
|
||||
|
||||
for network in custom_id_networks:
|
||||
print(network.name, network.id, network.attrs)
|
||||
|
||||
print("\nNetwork With Custom Filter:\n")
|
||||
|
||||
filtered_networks = docker_client.networks.list(
|
||||
names=["gitea_default"],
|
||||
filters={"driver": "bridge"}
|
||||
)
|
||||
|
||||
for network in filtered_networks:
|
||||
if network.attrs["Driver"]:
|
||||
print(network.name, network.id)
|
||||
|
||||
print("\nNetwork With Custom Filter (Greedy):\n")
|
||||
|
||||
filtered_networks = docker_client.networks.list(
|
||||
names=["gitea_default"],
|
||||
filters={"driver": "bridge"},
|
||||
greedy=True
|
||||
)
|
||||
|
||||
for network in filtered_networks:
|
||||
if network.attrs["Driver"]:
|
||||
print(network.name, network.id, network.attrs)
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
||||
0
Docs/Libs/Docker/06-Volumes.md
Normal file
0
Docs/Libs/Docker/06-Volumes.md
Normal file
0
Docs/Libs/Docker/07-Monitoring.md
Normal file
0
Docs/Libs/Docker/07-Monitoring.md
Normal file
0
Docs/Libs/Docker/08-Error-Handeling.md
Normal file
0
Docs/Libs/Docker/08-Error-Handeling.md
Normal file
0
Docs/Libs/Docker/09-Compose.md
Normal file
0
Docs/Libs/Docker/09-Compose.md
Normal file
127
Docs/Libs/FastAPI/04-Post-Method.md
Normal file
127
Docs/Libs/FastAPI/04-Post-Method.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# FastAPI – POST Endpoint and JSON Input
|
||||
|
||||
This section demonstrates how to handle **POST requests** in FastAPI to create new resources using request data and return JSON responses.
|
||||
|
||||
---
|
||||
|
||||
## Example Application (POST Request)
|
||||
|
||||
Extend `main.py` with the following code:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users = [
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def home_page():
|
||||
return {"msg": "API is working"}
|
||||
|
||||
|
||||
@app.post("/new_user")
|
||||
def create_user(name: str, age: int):
|
||||
new_user = {"name": name, "age": age}
|
||||
users.append(new_user)
|
||||
return {"msg": "User created successfully"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Overview
|
||||
|
||||
### POST Endpoint Definition
|
||||
|
||||
```python
|
||||
@app.post("/new_user")
|
||||
def create_user(name: str, age: int):
|
||||
```
|
||||
|
||||
* Registers an HTTP **POST** endpoint at `/new_user`
|
||||
* Accepts input parameters:
|
||||
|
||||
* `name` → string
|
||||
* `age` → integer
|
||||
* FastAPI automatically validates input types
|
||||
|
||||
---
|
||||
|
||||
### Creating a New Resource
|
||||
|
||||
```python
|
||||
new_user = {"name": name, "age": age}
|
||||
users.append(new_user)
|
||||
```
|
||||
|
||||
* Constructs a new user object
|
||||
* Appends it to the in-memory `users` list
|
||||
* Simulates creating a record in a database
|
||||
|
||||
---
|
||||
|
||||
### JSON Response
|
||||
|
||||
```python
|
||||
return {"msg": "User created successfully"}
|
||||
```
|
||||
|
||||
* Returns a structured JSON response
|
||||
* Automatically serialized by FastAPI
|
||||
|
||||
---
|
||||
|
||||
## Example Request
|
||||
|
||||
### Using `curl`
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/new_user?name=ali&age=25"
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "User created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verifying the Result
|
||||
|
||||
After creating a user, retrieve the updated list:
|
||||
|
||||
```http
|
||||
GET /users
|
||||
```
|
||||
|
||||
Response will now include the newly added user.
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the FastAPI service using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* POST requests should be used to create resources
|
||||
* Avoid modifying in-memory data in production environments
|
||||
* Use request bodies with Pydantic models instead of query parameters for real APIs
|
||||
* Return appropriate HTTP status codes (`201 Created`)
|
||||
* Validate and sanitize all client-provided input
|
||||
* Replace in-memory storage with persistent databases
|
||||
|
||||
162
Docs/Libs/FastAPI/05-Put-Method.md
Normal file
162
Docs/Libs/FastAPI/05-Put-Method.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# FastAPI – PUT Method
|
||||
|
||||
This document demonstrates how to use the **HTTP PUT method** in FastAPI to update an existing resource.
|
||||
It builds on previous GET and POST examples and completes a basic CRUD-style workflow.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users = [
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def home_page():
|
||||
return {"msg": "API is working"}
|
||||
|
||||
|
||||
@app.get("/users")
|
||||
def show_users():
|
||||
return users
|
||||
|
||||
|
||||
@app.post("/create_user")
|
||||
def create_user(name: str, age: int):
|
||||
new_user = {"name": name, "age": age}
|
||||
users.append(new_user)
|
||||
return {"msg": f"user {name} with age {age} created"}
|
||||
|
||||
|
||||
@app.put("/update_user/{target_name}")
|
||||
def update_user(target_name: str, age: int):
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
user["age"] = age
|
||||
return {"msg": f"user {target_name} updated"}
|
||||
return {"msg": "user not found"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Overview
|
||||
|
||||
### PUT Endpoint Definition
|
||||
|
||||
```python
|
||||
@app.put("/update_user/{target_name}")
|
||||
def update_user(target_name: str, age: int):
|
||||
```
|
||||
|
||||
* Registers an HTTP **PUT** endpoint
|
||||
* `target_name` is a **path parameter**
|
||||
* `age` is a **query parameter**
|
||||
* Used to update an existing user’s data
|
||||
|
||||
---
|
||||
|
||||
### Update Logic
|
||||
|
||||
```python
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
user["age"] = age
|
||||
```
|
||||
|
||||
* Iterates over the in-memory users list
|
||||
* Matches user by name
|
||||
* Updates the `age` field in place
|
||||
|
||||
---
|
||||
|
||||
### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "user abbas updated"
|
||||
}
|
||||
```
|
||||
|
||||
Returned when the target user exists and is updated successfully.
|
||||
|
||||
---
|
||||
|
||||
### Failure Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "user not found"
|
||||
}
|
||||
```
|
||||
|
||||
Returned when no matching user exists.
|
||||
|
||||
---
|
||||
|
||||
## Example Requests
|
||||
|
||||
### Update User Age (PUT)
|
||||
|
||||
```bash
|
||||
curl -X PUT "http://localhost:8000/update_user/abbas?age=25"
|
||||
```
|
||||
|
||||
### Verify Update
|
||||
|
||||
```http
|
||||
GET /users
|
||||
```
|
||||
|
||||
Updated response:
|
||||
|
||||
```json
|
||||
[
|
||||
{"name": "abbas", "age": 25},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HTTP Method Summary
|
||||
|
||||
| Method | Endpoint | Purpose |
|
||||
| ------ | --------------------- | ----------------------- |
|
||||
| GET | `/` | Health check |
|
||||
| GET | `/users` | Retrieve all users |
|
||||
| POST | `/create_user` | Create a new user |
|
||||
| PUT | `/update_user/{name}` | Update an existing user |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Use **PUT** for full updates and **PATCH** for partial updates
|
||||
* Return proper HTTP status codes (`404`, `200`, `201`)
|
||||
* Avoid using in-memory data stores in production
|
||||
* Use Pydantic models for request bodies instead of query parameters
|
||||
* Add input validation and error handling
|
||||
* Separate routes, services, and models as the project grows
|
||||
* Make PUT operations idempotent
|
||||
|
||||
172
Docs/Libs/FastAPI/06-Delete-Method.md
Normal file
172
Docs/Libs/FastAPI/06-Delete-Method.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# FastAPI – DELETE Method (Remove Resource)
|
||||
|
||||
This document demonstrates how to use the **HTTP DELETE method** in FastAPI to remove an existing resource.
|
||||
It completes the CRUD workflow using **GET, POST, PUT, and DELETE** operations.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users = [
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def home_page():
|
||||
return {"msg": "API is working"}
|
||||
|
||||
|
||||
@app.get("/users")
|
||||
def show_users():
|
||||
return users
|
||||
|
||||
|
||||
@app.post("/create_user")
|
||||
def create_user(name: str, age: int):
|
||||
new_user = {"name": name, "age": age}
|
||||
users.append(new_user)
|
||||
return {"msg": f"user {name} with age {age} created"}
|
||||
|
||||
|
||||
@app.put("/update_user/{target_name}")
|
||||
def update_user(target_name: str, age: int):
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
user["age"] = age
|
||||
return {"msg": f"user {target_name} updated"}
|
||||
return {"msg": "user not found"}
|
||||
|
||||
|
||||
@app.delete("/delete_user/{target_name}")
|
||||
def delete_user(target_name: str):
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
users.remove(user)
|
||||
return {"msg": f"user {target_name} deleted"}
|
||||
return {"msg": "user not found"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Overview
|
||||
|
||||
### DELETE Endpoint Definition
|
||||
|
||||
```python
|
||||
@app.delete("/delete_user/{target_name}")
|
||||
def delete_user(target_name: str):
|
||||
```
|
||||
|
||||
* Registers an HTTP **DELETE** endpoint
|
||||
* `target_name` is a **path parameter**
|
||||
* Used to remove a user from the data store
|
||||
|
||||
---
|
||||
|
||||
### Delete Logic
|
||||
|
||||
```python
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
users.remove(user)
|
||||
```
|
||||
|
||||
* Iterates through the users list
|
||||
* Finds a matching user by name
|
||||
* Removes the user from the list
|
||||
|
||||
---
|
||||
|
||||
### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "user abbas deleted"
|
||||
}
|
||||
```
|
||||
|
||||
Returned when the user is successfully removed.
|
||||
|
||||
---
|
||||
|
||||
### Failure Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "user not found"
|
||||
}
|
||||
```
|
||||
|
||||
Returned when the specified user does not exist.
|
||||
|
||||
---
|
||||
|
||||
## Example Requests
|
||||
|
||||
### Delete a User
|
||||
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8000/delete_user/abbas"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Verify Deletion
|
||||
|
||||
```http
|
||||
GET /users
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
[
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service with `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CRUD Endpoint Summary
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| ------ | --------------------- | ------------------ |
|
||||
| GET | `/` | Health check |
|
||||
| GET | `/users` | Retrieve all users |
|
||||
| POST | `/create_user` | Create a user |
|
||||
| PUT | `/update_user/{name}` | Update a user |
|
||||
| DELETE | `/delete_user/{name}` | Delete a user |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Use **DELETE** only for resource removal
|
||||
* Return appropriate HTTP status codes (`204`, `404`)
|
||||
* Ensure delete operations are idempotent
|
||||
* Avoid modifying in-memory data in production
|
||||
* Add authentication and authorization for destructive operations
|
||||
* Log delete actions for auditability
|
||||
* Use database transactions when deleting persistent data
|
||||
|
||||
199
Docs/Libs/FastAPI/07-Query-Parameter.md
Normal file
199
Docs/Libs/FastAPI/07-Query-Parameter.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# FastAPI – Query Parameters
|
||||
|
||||
This document demonstrates how to use **query parameters** in FastAPI using multiple typing styles, including:
|
||||
|
||||
* Native Python union types (`str | None`)
|
||||
* `Optional` from `typing`
|
||||
* `Annotated` with `Query` validation
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, Query
|
||||
from typing import Optional, Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users = [
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "abbas", "age": 32},
|
||||
{"name": "abbas", "age": 54},
|
||||
{"name": "abbas", "age": 15},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def home_page():
|
||||
return {"msg": "API is working"}
|
||||
|
||||
|
||||
@app.get("/user")
|
||||
def get_users(target_name: str | None = None):
|
||||
if target_name:
|
||||
return [item for item in users if item["name"] == target_name]
|
||||
return users
|
||||
|
||||
|
||||
@app.get("/user2")
|
||||
def get_users_optional(target_name: Optional[str] = None):
|
||||
if target_name:
|
||||
return [item for item in users if item["name"] == target_name]
|
||||
return users
|
||||
|
||||
|
||||
@app.get("/user3")
|
||||
def get_users_annotated(
|
||||
target_name: Annotated[str | None, Query(max_length=50)] = None
|
||||
):
|
||||
if target_name:
|
||||
return [item for item in users if item["name"] == target_name]
|
||||
return users
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Query Parameter Overview
|
||||
|
||||
Query parameters are key-value pairs appended to the URL after `?`.
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
/user?target_name=abbas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Defined Endpoints
|
||||
|
||||
### 1. Basic Query Parameter (Python Union Type)
|
||||
|
||||
```python
|
||||
@app.get("/user")
|
||||
def get_users(target_name: str | None = None):
|
||||
```
|
||||
|
||||
* Uses Python 3.10+ union syntax
|
||||
* `target_name` is optional
|
||||
* If not provided, all users are returned
|
||||
|
||||
**Example Request:**
|
||||
|
||||
```
|
||||
GET /user?target_name=abbas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Optional Query Parameter (`typing.Optional`)
|
||||
|
||||
```python
|
||||
@app.get("/user2")
|
||||
def get_users_optional(target_name: Optional[str] = None):
|
||||
```
|
||||
|
||||
* Uses `Optional[str]` for compatibility with older Python versions
|
||||
* Behavior is identical to the first endpoint
|
||||
|
||||
**Example Request:**
|
||||
|
||||
```
|
||||
GET /user2?target_name=abbas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Validated Query Parameter (`Annotated + Query`)
|
||||
|
||||
```python
|
||||
@app.get("/user3")
|
||||
def get_users_annotated(
|
||||
target_name: Annotated[str | None, Query(max_length=50)] = None
|
||||
):
|
||||
```
|
||||
|
||||
* Uses `Annotated` for metadata binding
|
||||
* Adds validation rules:
|
||||
|
||||
* `max_length=50`
|
||||
* Automatically returns validation errors if constraints are violated
|
||||
|
||||
**Example Invalid Request:**
|
||||
|
||||
```
|
||||
GET /user3?target_name=verylongnamethatexceedslimit...
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "string_too_long",
|
||||
"loc": ["query", "target_name"],
|
||||
"msg": "String should have at most 50 characters",
|
||||
"input": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example Responses
|
||||
|
||||
### Filtered Response
|
||||
|
||||
```json
|
||||
[
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "abbas", "age": 32},
|
||||
{"name": "abbas", "age": 54},
|
||||
{"name": "abbas", "age": 15}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Default Response (No Query Parameter)
|
||||
|
||||
```json
|
||||
[
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "abbas", "age": 32},
|
||||
{"name": "abbas", "age": 54},
|
||||
{"name": "abbas", "age": 15},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Use query parameters for filtering and searching
|
||||
* Always provide defaults for optional parameters
|
||||
* Use `Query()` for validation and constraints
|
||||
* Return full datasets when no filters are applied
|
||||
* Avoid large in-memory datasets in production
|
||||
* Use pagination for large result sets
|
||||
* Combine query parameters with database queries in real systems
|
||||
|
||||
192
Docs/Libs/FastAPI/08.Status-Codes.md
Normal file
192
Docs/Libs/FastAPI/08.Status-Codes.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# FastAPI – HTTP Status Codes and Error Handling
|
||||
|
||||
This document demonstrates how to use **HTTP status codes** in FastAPI to accurately represent the result of an API operation.
|
||||
Correct status codes improve API reliability, observability, and client-side behavior.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, status, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users = [
|
||||
{"name": "abbas", "age": 20},
|
||||
{"name": "mmd", "age": 37},
|
||||
{"name": "asghar", "age": 19},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/", status_code=status.HTTP_200_OK)
|
||||
def home_page():
|
||||
return {"msg": "API is working"}
|
||||
|
||||
|
||||
@app.get("/users", status_code=status.HTTP_200_OK)
|
||||
def show_users():
|
||||
return users
|
||||
|
||||
|
||||
@app.post("/create_user", status_code=status.HTTP_201_CREATED)
|
||||
def create_user(name: str, age: int):
|
||||
new_user = {"name": name, "age": age}
|
||||
users.append(new_user)
|
||||
return {"msg": f"user {name} with age {age} created"}
|
||||
|
||||
|
||||
@app.put("/update_user/{target_name}", status_code=status.HTTP_202_ACCEPTED)
|
||||
def update_user(target_name: str, age: int):
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
user["age"] = age
|
||||
return {"msg": f"user {target_name} updated"}
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="User not found"
|
||||
)
|
||||
|
||||
|
||||
@app.delete("/delete_user/{target_name}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_user(target_name: str):
|
||||
for user in users:
|
||||
if user["name"] == target_name:
|
||||
users.remove(user)
|
||||
return
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="User not found"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Status Code Overview
|
||||
|
||||
HTTP status codes communicate the result of an API request to clients and monitoring systems.
|
||||
|
||||
| Code | Meaning | Usage |
|
||||
| ---- | ---------- | --------------------------------- |
|
||||
| 200 | OK | Successful GET requests |
|
||||
| 201 | Created | Resource successfully created |
|
||||
| 202 | Accepted | Update request accepted |
|
||||
| 204 | No Content | Resource deleted successfully |
|
||||
| 404 | Not Found | Requested resource does not exist |
|
||||
|
||||
---
|
||||
|
||||
## Endpoint Behavior
|
||||
|
||||
### Root Endpoint
|
||||
|
||||
```http
|
||||
GET /
|
||||
```
|
||||
|
||||
* Returns HTTP **200 OK**
|
||||
* Used as a health check
|
||||
|
||||
---
|
||||
|
||||
### Create User
|
||||
|
||||
```http
|
||||
POST /create_user
|
||||
```
|
||||
|
||||
* Returns HTTP **201 Created**
|
||||
* Indicates successful resource creation
|
||||
|
||||
---
|
||||
|
||||
### Update User
|
||||
|
||||
```http
|
||||
PUT /update_user/{target_name}
|
||||
```
|
||||
|
||||
* Returns HTTP **202 Accepted** on success
|
||||
* Raises HTTP **404 Not Found** if user does not exist
|
||||
|
||||
---
|
||||
|
||||
### Delete User
|
||||
|
||||
```http
|
||||
DELETE /delete_user/{target_name}
|
||||
```
|
||||
|
||||
* Returns HTTP **204 No Content** on success
|
||||
* Returns no response body
|
||||
* Raises HTTP **404 Not Found** if user does not exist
|
||||
|
||||
---
|
||||
|
||||
## Error Handling with `HTTPException`
|
||||
|
||||
```python
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="User not found"
|
||||
)
|
||||
```
|
||||
|
||||
* Immediately stops request processing
|
||||
* Returns structured error responses
|
||||
* Automatically serialized by FastAPI
|
||||
|
||||
**Error Response Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example Requests
|
||||
|
||||
### Create User
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/create_user?name=ali&age=25"
|
||||
```
|
||||
|
||||
### Update User
|
||||
|
||||
```bash
|
||||
curl -X PUT "http://localhost:8000/update_user/ali?age=30"
|
||||
```
|
||||
|
||||
### Delete User
|
||||
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8000/delete_user/ali"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the application using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Always return meaningful HTTP status codes
|
||||
* Use `status` module instead of hard-coded numbers
|
||||
* Use `HTTPException` for predictable error handling
|
||||
* Do not return response bodies with `204 No Content`
|
||||
* Align status codes with REST conventions
|
||||
* Ensure monitoring systems rely on status codes, not messages
|
||||
* Standardize error formats across services
|
||||
|
||||
122
Docs/Libs/FastAPI/09-Json-Responce.md
Normal file
122
Docs/Libs/FastAPI/09-Json-Responce.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# FastAPI – JSONResponse (Explicit JSON Responses)
|
||||
|
||||
This document demonstrates how to return **explicit JSON responses** in FastAPI using `JSONResponse`.
|
||||
While FastAPI automatically serializes dictionaries to JSON, `JSONResponse` is useful when you need **full control** over the response.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, status
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def home():
|
||||
return JSONResponse(
|
||||
content={"msg": "API is working"},
|
||||
status_code=status.HTTP_200_OK
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Why Use `JSONResponse`
|
||||
|
||||
FastAPI automatically converts Python dictionaries into JSON responses.
|
||||
However, `JSONResponse` is useful when you need to:
|
||||
|
||||
* Explicitly control the response type
|
||||
* Set custom status codes dynamically
|
||||
* Customize headers
|
||||
* Return non-standard JSON structures
|
||||
* Override default response behavior
|
||||
|
||||
---
|
||||
|
||||
## Response Behavior
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
GET /
|
||||
```
|
||||
|
||||
### Response Body
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "API is working"
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Status Code
|
||||
|
||||
```
|
||||
200 OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comparison: Default Response vs JSONResponse
|
||||
|
||||
### Default FastAPI Response
|
||||
|
||||
```python
|
||||
@app.get("/")
|
||||
def home():
|
||||
return {"msg": "API is working"}
|
||||
```
|
||||
|
||||
* Automatically serialized to JSON
|
||||
* Simpler and recommended for most cases
|
||||
|
||||
---
|
||||
|
||||
### Explicit JSONResponse
|
||||
|
||||
```python
|
||||
return JSONResponse(
|
||||
content={"msg": "API is working"},
|
||||
status_code=status.HTTP_200_OK
|
||||
)
|
||||
```
|
||||
|
||||
* Explicit control over response
|
||||
* Useful for advanced use cases
|
||||
|
||||
---
|
||||
|
||||
## When to Use `JSONResponse`
|
||||
|
||||
* Returning conditional status codes
|
||||
* Adding custom headers
|
||||
* Returning responses outside standard request flow
|
||||
* Building middleware or exception handlers
|
||||
* Integrating with legacy systems
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the application using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Prefer returning dictionaries for simple APIs
|
||||
* Use `JSONResponse` only when additional control is required
|
||||
* Keep response formats consistent across endpoints
|
||||
* Avoid mixing response styles unnecessarily
|
||||
* Use response models for structured APIs
|
||||
|
||||
238
Docs/Libs/FastAPI/10-Query-Path--AdvancedParm.md
Normal file
238
Docs/Libs/FastAPI/10-Query-Path--AdvancedParm.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# FastAPI – Advanced Path and Query Parameters
|
||||
|
||||
This document demonstrates advanced usage of **path parameters** and **query parameters** in FastAPI, including:
|
||||
|
||||
* Validation rules
|
||||
* Aliases
|
||||
* Descriptions for OpenAPI
|
||||
* Deprecation flags
|
||||
* Clear separation of responsibilities between path and query parameters
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, Query, Path, status
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users_db = [
|
||||
{"id": "1", "name": "radin"}
|
||||
]
|
||||
|
||||
|
||||
@app.get("/user/{target_id}")
|
||||
def get_user_by_path(
|
||||
target_id: int = Path(
|
||||
...,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID",
|
||||
deprecated=True,
|
||||
)
|
||||
):
|
||||
for item in users_db:
|
||||
if int(item["id"]) == target_id:
|
||||
return JSONResponse(
|
||||
content={"msg": f"Your target user name is {item['name']}"},
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
content={"msg": "user not found"},
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/user")
|
||||
def get_user_by_query(
|
||||
target: int | None = Query(
|
||||
default=None,
|
||||
gt=0,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID",
|
||||
)
|
||||
):
|
||||
for item in users_db:
|
||||
if item["id"] == str(target):
|
||||
return JSONResponse(
|
||||
content={"msg": f"Your target user name is {item['name']}"},
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
content={"msg": "user not found"},
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path Parameter Example
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
GET /user/{target_id}
|
||||
```
|
||||
|
||||
**Example Request:**
|
||||
|
||||
```
|
||||
/user/1
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Always required
|
||||
* Defined as part of the route
|
||||
* Used to identify a specific resource
|
||||
* Missing value results in **404 Not Found**
|
||||
* Supports validation, aliases, and documentation metadata
|
||||
|
||||
---
|
||||
|
||||
## Query Parameter Example
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
GET /user?User%20ID=1
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Optional by default
|
||||
* Used for filtering and searching
|
||||
* Not part of the route path
|
||||
* Supports validation and documentation metadata
|
||||
* Uses default values when not provided
|
||||
|
||||
---
|
||||
|
||||
## Advanced Parameter Configuration
|
||||
|
||||
### `Path()` Example
|
||||
|
||||
```python
|
||||
Path(
|
||||
...,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID",
|
||||
deprecated=True
|
||||
)
|
||||
```
|
||||
|
||||
* `...` → parameter is required
|
||||
* `alias` → custom name in documentation and URL
|
||||
* `description` → shown in Swagger UI
|
||||
* `deprecated` → marked as deprecated in OpenAPI
|
||||
|
||||
---
|
||||
|
||||
### `Query()` Example
|
||||
|
||||
```python
|
||||
Query(
|
||||
default=None,
|
||||
gt=0,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID"
|
||||
)
|
||||
```
|
||||
|
||||
* `gt=0` → value must be greater than zero
|
||||
* Optional parameter with validation rules
|
||||
* Appears in API documentation
|
||||
|
||||
---
|
||||
|
||||
## Path vs Query Parameters
|
||||
|
||||
### 1. Path Parameters
|
||||
|
||||
**What they are**
|
||||
Values embedded directly in the URL path that identify a specific resource.
|
||||
|
||||
**When to use**
|
||||
When the parameter is mandatory and uniquely identifies a resource.
|
||||
|
||||
**Example URL**
|
||||
|
||||
```
|
||||
/users/42
|
||||
```
|
||||
|
||||
**FastAPI Example**
|
||||
|
||||
```python
|
||||
@app.get("/users/{user_id}")
|
||||
def get_user(user_id: int):
|
||||
return {"user_id": user_id}
|
||||
```
|
||||
|
||||
**Key Points**
|
||||
|
||||
* Always required
|
||||
* Part of the route definition
|
||||
* Used for IDs and unique identifiers
|
||||
* Missing value → 404 error
|
||||
|
||||
---
|
||||
|
||||
### 2. Query Parameters
|
||||
|
||||
**What they are**
|
||||
Key-value pairs appended to the URL after `?`.
|
||||
|
||||
**When to use**
|
||||
For optional filtering, searching, sorting, and pagination.
|
||||
|
||||
**Example URL**
|
||||
|
||||
```
|
||||
/users?limit=10&active=true
|
||||
```
|
||||
|
||||
**FastAPI Example**
|
||||
|
||||
```python
|
||||
@app.get("/users")
|
||||
def list_users(limit: int = 10, active: bool = True):
|
||||
return {"limit": limit, "active": active}
|
||||
```
|
||||
|
||||
**Key Points**
|
||||
|
||||
* Optional by default
|
||||
* Not part of the route path
|
||||
* Used to modify or filter results
|
||||
* Defaults are used when missing
|
||||
|
||||
---
|
||||
|
||||
## Side-by-Side Comparison
|
||||
|
||||
| Feature | Path Parameter | Query Parameter |
|
||||
| ------------- | ------------------- | --------------------------------- |
|
||||
| Location | Inside URL path | After `?` |
|
||||
| Required | Yes | No (by default) |
|
||||
| Purpose | Identify a resource | Filter or modify results |
|
||||
| Example | `/items/5` | `/items?limit=10` |
|
||||
| Missing Value | 404 Not Found | Default value or validation error |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Use path parameters for resource identification
|
||||
* Use query parameters for filtering and modifiers
|
||||
* Avoid spaces in parameter aliases for production APIs
|
||||
* Deprecate parameters instead of removing them
|
||||
* Use validation (`gt`, `lt`, `regex`) for safer APIs
|
||||
* Keep URL design consistent across services
|
||||
* Prefer response models over manual JSON responses when possible
|
||||
|
||||
226
Docs/Libs/FastAPI/11-Post-Types.md
Normal file
226
Docs/Libs/FastAPI/11-Post-Types.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# POST Request Types and Content Types
|
||||
|
||||
This document explains the **body types of POST requests** and the most common **Content-Types** used in APIs.
|
||||
Understanding these formats is essential for building interoperable and production-ready FastAPI services.
|
||||
|
||||
---
|
||||
|
||||
## POST Request Body Types
|
||||
|
||||
A POST request sends data to the server in the **request body**.
|
||||
The format of this data is defined by the **Content-Type** header.
|
||||
|
||||
---
|
||||
|
||||
## 1. Form Data
|
||||
|
||||
### Description
|
||||
|
||||
Used primarily for HTML form submissions. Common in browser-based applications.
|
||||
|
||||
### Content-Type
|
||||
|
||||
```
|
||||
application/x-www-form-urlencoded
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
multipart/form-data
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Key-value pairs
|
||||
* Supports file uploads (multipart)
|
||||
* Common in login and upload forms
|
||||
|
||||
### Example
|
||||
|
||||
```http
|
||||
POST /login
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
username=admin&password=secret
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. JSON
|
||||
|
||||
### Description
|
||||
|
||||
The most common data format for REST APIs.
|
||||
|
||||
### Content-Type
|
||||
|
||||
```
|
||||
application/json
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Structured, readable, and language-independent
|
||||
* Easily parsed and validated
|
||||
* Default choice for FastAPI APIs
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "abbas",
|
||||
"age": 25
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. XML
|
||||
|
||||
### Description
|
||||
|
||||
An older but still widely used data format in enterprise systems and legacy APIs.
|
||||
|
||||
### Content-Type
|
||||
|
||||
```
|
||||
application/xml
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
text/xml
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Verbose and schema-driven
|
||||
* Common in SOAP-based services
|
||||
* Less common in modern REST APIs
|
||||
|
||||
### Example
|
||||
|
||||
```xml
|
||||
<user>
|
||||
<name>abbas</name>
|
||||
<age>25</age>
|
||||
</user>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Plain Text
|
||||
|
||||
### Description
|
||||
|
||||
Used when sending raw text without structure.
|
||||
|
||||
### Content-Type
|
||||
|
||||
```
|
||||
text/plain
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* No schema or structure
|
||||
* Useful for logs, messages, or simple payloads
|
||||
* Requires custom parsing on the server
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
Hello FastAPI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Binary Data
|
||||
|
||||
### Description
|
||||
|
||||
Used for sending non-textual data such as images, videos, or files.
|
||||
|
||||
### Content-Type
|
||||
|
||||
```
|
||||
application/octet-stream
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Raw binary format
|
||||
* Common for file uploads and downloads
|
||||
* Requires stream handling
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
(binary file data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. GraphQL
|
||||
|
||||
### Description
|
||||
|
||||
GraphQL uses POST requests to execute queries and mutations.
|
||||
|
||||
### Content-Type
|
||||
|
||||
```
|
||||
application/json
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* Single endpoint
|
||||
* Flexible client-driven queries
|
||||
* Requires GraphQL server support
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "query { users { id name } }"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Content Types Summary
|
||||
|
||||
| Content-Type | Usage |
|
||||
| ----------------------------------- | -------------------------- |
|
||||
| `application/json` | REST APIs (default) |
|
||||
| `application/x-www-form-urlencoded` | HTML forms |
|
||||
| `multipart/form-data` | Forms with file uploads |
|
||||
| `application/xml` | Legacy / SOAP APIs |
|
||||
| `text/plain` | Raw text |
|
||||
| `application/octet-stream` | Binary data |
|
||||
| `application/graphql` | GraphQL (rare, often JSON) |
|
||||
|
||||
---
|
||||
|
||||
## FastAPI Considerations
|
||||
|
||||
* JSON is the recommended default for FastAPI
|
||||
* Use Pydantic models for JSON validation
|
||||
* Use `Form()` and `File()` for form and file uploads
|
||||
* Always validate content type in production APIs
|
||||
* Document supported content types clearly
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Choose the simplest format that meets requirements
|
||||
* Prefer `application/json` for APIs
|
||||
* Avoid XML unless required by integration
|
||||
* Secure file uploads (size limits, scanning)
|
||||
* Validate all incoming request bodies
|
||||
* Be explicit about supported Content-Types
|
||||
|
||||
165
Docs/Libs/FastAPI/12-From-Post.md
Normal file
165
Docs/Libs/FastAPI/12-From-Post.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# FastAPI – POST Requests with Form Data
|
||||
|
||||
This document demonstrates how to handle **form-based POST requests** in FastAPI using the `Form()` dependency.
|
||||
Form data is commonly used in **HTML forms**, authentication flows, and legacy systems.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, Form, status
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users_db = [
|
||||
{"id": "1", "name": "radin", "password": "123"}
|
||||
]
|
||||
|
||||
|
||||
@app.post("/user/")
|
||||
def get_user_from_form(
|
||||
target: int = Form(
|
||||
...,
|
||||
gt=0,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID",
|
||||
)
|
||||
):
|
||||
for item in users_db:
|
||||
if item["id"] == str(target):
|
||||
return JSONResponse(
|
||||
content={"msg": f"Your target user name is {item['name']}"},
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
content={"msg": "user not found"},
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Form Data Overview
|
||||
|
||||
### What is Form Data
|
||||
|
||||
Form data is sent in the request body using:
|
||||
|
||||
```
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
multipart/form-data
|
||||
```
|
||||
|
||||
FastAPI requires the `Form()` dependency to explicitly declare form inputs.
|
||||
|
||||
---
|
||||
|
||||
## Endpoint Behavior
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
POST /user/
|
||||
```
|
||||
|
||||
### Request Body (Form Data)
|
||||
|
||||
```
|
||||
User ID=1
|
||||
```
|
||||
|
||||
### Example Using `curl`
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/user/" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "User ID=1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Response Examples
|
||||
|
||||
### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "Your target user name is radin"
|
||||
}
|
||||
```
|
||||
|
||||
Status Code: **200 OK**
|
||||
|
||||
---
|
||||
|
||||
### Failure Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "user not found"
|
||||
}
|
||||
```
|
||||
|
||||
Status Code: **404 Not Found**
|
||||
|
||||
---
|
||||
|
||||
## `Form()` Parameter Configuration
|
||||
|
||||
```python
|
||||
Form(
|
||||
...,
|
||||
gt=0,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID"
|
||||
)
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
| ------------- | ------------------------------- |
|
||||
| `...` | Field is required |
|
||||
| `gt=0` | Value must be greater than zero |
|
||||
| `alias` | Custom form field name |
|
||||
| `description` | Displayed in API docs |
|
||||
|
||||
---
|
||||
|
||||
## Swagger / OpenAPI Behavior
|
||||
|
||||
* Form fields appear as input fields
|
||||
* Aliases are reflected in the UI
|
||||
* Validation rules are enforced automatically
|
||||
* Errors are returned with clear messages
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Use form data only when required (e.g. HTML forms)
|
||||
* Prefer JSON for APIs and services
|
||||
* Avoid exposing sensitive fields in plain form data
|
||||
* Use HTTPS for all form submissions
|
||||
* Validate and sanitize all inputs
|
||||
* Use authentication and hashing for passwords
|
||||
* Do not store credentials in plain text
|
||||
|
||||
161
Docs/Libs/FastAPI/13-Body-Post.md
Normal file
161
Docs/Libs/FastAPI/13-Body-Post.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# FastAPI – POST Requests with JSON Body (`Body`)
|
||||
|
||||
This document demonstrates how to receive data from the **request body** using `Body()` in FastAPI.
|
||||
This approach is commonly used when clients send **JSON payloads**.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, Body, status
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users_db = [
|
||||
{"id": "1", "name": "radin", "password": "123"}
|
||||
]
|
||||
|
||||
|
||||
@app.post("/user/")
|
||||
def get_user_from_body(
|
||||
target: int = Body(
|
||||
...,
|
||||
embed=True,
|
||||
gt=0,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID",
|
||||
)
|
||||
):
|
||||
for item in users_db:
|
||||
if item["id"] == str(target):
|
||||
return JSONResponse(
|
||||
content={"msg": f"Your target user name is {item['name']}"},
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
content={"msg": "user not found"},
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JSON Body Overview
|
||||
|
||||
### What Is a Request Body
|
||||
|
||||
The request body contains structured data sent by the client, most commonly as **JSON**.
|
||||
|
||||
**Content-Type:**
|
||||
|
||||
```
|
||||
application/json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Request Format
|
||||
|
||||
### Example JSON Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"User ID": 1
|
||||
}
|
||||
```
|
||||
|
||||
* `embed=True` requires the value to be wrapped inside a JSON object
|
||||
* The key name is controlled by the `alias`
|
||||
|
||||
---
|
||||
|
||||
## Example Request Using `curl`
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/user/" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"User ID": 1}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Response Examples
|
||||
|
||||
### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "Your target user name is radin"
|
||||
}
|
||||
```
|
||||
|
||||
Status Code: **200 OK**
|
||||
|
||||
---
|
||||
|
||||
### Failure Response
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "user not found"
|
||||
}
|
||||
```
|
||||
|
||||
Status Code: **404 Not Found**
|
||||
|
||||
---
|
||||
|
||||
## `Body()` Parameter Configuration
|
||||
|
||||
```python
|
||||
Body(
|
||||
...,
|
||||
embed=True,
|
||||
gt=0,
|
||||
alias="User ID",
|
||||
description="Enter target unique ID"
|
||||
)
|
||||
```
|
||||
|
||||
| Parameter | Purpose |
|
||||
| ------------- | -------------------------------- |
|
||||
| `...` | Field is required |
|
||||
| `embed=True` | Wraps the value in a JSON object |
|
||||
| `gt=0` | Validates input value |
|
||||
| `alias` | Custom JSON key name |
|
||||
| `description` | Shown in API documentation |
|
||||
|
||||
---
|
||||
|
||||
## Swagger / OpenAPI Behavior
|
||||
|
||||
* JSON schema is automatically generated
|
||||
* Validation errors are returned if rules are violated
|
||||
* Aliases and descriptions appear in Swagger UI
|
||||
* Request body is clearly documented
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Prefer request body over query parameters for POST requests
|
||||
* Use Pydantic models instead of raw `Body()` for complex payloads
|
||||
* Keep JSON structures consistent
|
||||
* Avoid spaces in JSON keys for production APIs
|
||||
* Never send sensitive data in plain text
|
||||
* Use HTTPS for all JSON-based APIs
|
||||
167
Docs/Libs/FastAPI/14-File-Post.md
Normal file
167
Docs/Libs/FastAPI/14-File-Post.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# FastAPI – POST Requests with File Uploads
|
||||
|
||||
This document demonstrates how to handle **file uploads** in FastAPI.
|
||||
File uploads are essential for APIs that need to receive **images, documents, or binary data** from clients.
|
||||
|
||||
---
|
||||
|
||||
## Example Application
|
||||
|
||||
Create or update `main.py` with the following content:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, File, UploadFile, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from typing import List
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
users_db = [
|
||||
{"id": "1", "name": "radin", "password": "123"}
|
||||
]
|
||||
|
||||
|
||||
@app.post("/file")
|
||||
def upload_file_bytes(file: bytes = File(...)):
|
||||
"""
|
||||
Receive file as raw bytes.
|
||||
Returns the size of the uploaded file.
|
||||
"""
|
||||
return {"file_size": len(file)}
|
||||
|
||||
|
||||
@app.post("/uploadfile")
|
||||
def upload_file_uploadfile(file: UploadFile):
|
||||
"""
|
||||
Receive file as UploadFile.
|
||||
Returns filename, content type, and size.
|
||||
"""
|
||||
content = file.read()
|
||||
return {
|
||||
"filename": file.filename,
|
||||
"content_type": file.content_type,
|
||||
"file_size": len(content)
|
||||
}
|
||||
|
||||
|
||||
@app.post("/uploadmultifile")
|
||||
def upload_multiple_files(files: List[UploadFile]):
|
||||
"""
|
||||
Receive multiple files as UploadFile list.
|
||||
Returns filenames and content types.
|
||||
"""
|
||||
return [
|
||||
{"filename": file.filename, "content_type": file.content_type}
|
||||
for file in files
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Upload Methods
|
||||
|
||||
### 1. `File` as Bytes
|
||||
|
||||
* Accepts the uploaded file as raw bytes
|
||||
* Suitable for small files or direct in-memory processing
|
||||
* Fast but lacks metadata (filename, content type)
|
||||
|
||||
**Example Request (curl):**
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/file" \
|
||||
-F "file=@example.txt"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"file_size": 128
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. `UploadFile`
|
||||
|
||||
* Accepts file as `UploadFile` object
|
||||
* Provides metadata: `filename` and `content_type`
|
||||
* Supports `.read()`, `.write()`, and `.seek()` operations
|
||||
* More efficient for large files (uses spooled temporary files)
|
||||
|
||||
**Example Request (curl):**
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/uploadfile" \
|
||||
-F "file=@example.txt"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"filename": "example.txt",
|
||||
"content_type": "text/plain",
|
||||
"file_size": 128
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Multiple File Uploads
|
||||
|
||||
* Accepts a list of `UploadFile`
|
||||
* Allows uploading multiple files in one request
|
||||
* Useful for batch uploads or form submissions
|
||||
|
||||
**Example Request (curl):**
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/uploadmultifile" \
|
||||
-F "files=@file1.txt" \
|
||||
-F "files=@file2.txt"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{"filename": "file1.txt", "content_type": "text/plain"},
|
||||
{"filename": "file2.txt", "content_type": "text/plain"}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Content-Type
|
||||
|
||||
For file uploads, the request must include:
|
||||
|
||||
```
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
* Each file is sent as a separate part in the multipart request
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service using `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Use `UploadFile` for large or multiple files
|
||||
* Validate file type and size on the server
|
||||
* Avoid loading very large files fully into memory
|
||||
* Use HTTPS for secure file transfer
|
||||
* Store files in dedicated storage (S3, local disk, or DB)
|
||||
* Return clear metadata (filename, size, content type) to clients
|
||||
* Support multiple files when needed for batch operations
|
||||
121
Docs/Libs/FastAPI/15-Life-Span.md
Normal file
121
Docs/Libs/FastAPI/15-Life-Span.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# FastAPI – Application Lifespan (Startup & Shutdown Events)
|
||||
|
||||
FastAPI allows you to run code when your application **starts up** or **shuts down**.
|
||||
This is useful for initializing resources, database connections, caches, or background tasks.
|
||||
|
||||
---
|
||||
|
||||
## Deprecated Method: `@app.on_event`
|
||||
|
||||
Older FastAPI versions use the `@app.on_event` decorator for lifecycle events:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
def on_startup():
|
||||
print("App is loading")
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
def on_shutdown():
|
||||
print("App is shutting down")
|
||||
```
|
||||
|
||||
### Characteristics
|
||||
|
||||
* `startup` runs once when the app starts
|
||||
* `shutdown` runs once when the app is stopped
|
||||
* Works for synchronous and asynchronous functions
|
||||
* Still supported but **deprecated** in favor of the `lifespan` parameter
|
||||
|
||||
---
|
||||
|
||||
## Recommended Modern Approach: `lifespan` with `asynccontextmanager`
|
||||
|
||||
FastAPI now recommends using the `lifespan` parameter in the `FastAPI` constructor.
|
||||
This uses Python's `asynccontextmanager` to define a **single lifecycle context**.
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Code to run before the app starts
|
||||
print("App is loading")
|
||||
yield # Application runs after this point
|
||||
# Code to run after the app stops
|
||||
print("App is shutting down")
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Code **before `yield`** executes on startup
|
||||
2. Code **after `yield`** executes on shutdown
|
||||
3. Supports async operations, e.g., connecting to a database
|
||||
|
||||
---
|
||||
|
||||
## Example: Using Lifespan for Database Initialization
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
app.state.db = await connect_to_db()
|
||||
print("Database connected")
|
||||
yield
|
||||
await app.state.db.close()
|
||||
print("Database disconnected")
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
```
|
||||
|
||||
* `app.state` is used to store shared resources
|
||||
* Clean startup and shutdown handling
|
||||
* Ensures proper resource cleanup
|
||||
|
||||
---
|
||||
|
||||
## Benefits of the Lifespan Approach
|
||||
|
||||
* Centralized lifecycle management
|
||||
* Cleaner async support
|
||||
* Avoids multiple scattered `@app.on_event` decorators
|
||||
* Better for testing and production-ready apps
|
||||
|
||||
---
|
||||
|
||||
## Running the Application
|
||||
|
||||
Start the service with `uvicorn`:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
* On startup, `App is loading` prints to the console
|
||||
* On shutdown (Ctrl+C), `App is shutting down` prints to the console
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* Always use `lifespan` for new applications
|
||||
* Use `app.state` to store shared resources
|
||||
* Close database connections, caches, or background services in shutdown
|
||||
* Keep startup logic lightweight to avoid blocking the server
|
||||
* Avoid printing in production; use logging instead
|
||||
|
||||
---
|
||||
|
||||
This approach provides a **modern, production-ready pattern** for managing application startup and shutdown events in FastAPI.
|
||||
|
||||
Reference in New Issue
Block a user