Skip to content

Latest commit

 

History

History
863 lines (544 loc) · 26.3 KB

File metadata and controls

863 lines (544 loc) · 26.3 KB

Terms

Web

REST = Representational State Transfer

  • REST is s an architecture style for distributed systems, especially web APIs. It is not a protocol, framework, or data format.

  • Rest is usually implemented over HTTP, although it is not strictly tied to HTTP.

  • A system is RESTful when it follows the REST architectural constraints:

    • Client-Server:

      The client and sever have separate responsibilities. The client handles the user interface, while the server stores data and implements application logic.

    • Stateless:

      Every request must contain all information required to process it. The server does not rely on client session state stored between requests.

    • Cacheable:

      Server responses indicate whether they may be cached. Caching can reduce server load and improve performance.

    • Uniform Interface:

      Clients interact with resources though a consistent interface. This includes resources identified by URIs, representations of resources (commonly JSON), self-described requests and responses, resource manipulation though representations, hypermedia links (when applicable).

    • Layered System:

      Intermediaries such as proxies, gateways, load balancers, and caches may exist between the client and the server. The client does not need to know whether it communicates directly with the final server.

    • Code on Demand (optional):

      The server may send executable code to the client, such aas JavaScript.

RESTful API

A RESTful API is an API designed according to the REST architectural constraints.

HTTP = HyperText Transfer Protocol

  • HTTP is an application-layer protocol.

  • Defines how requests and responses are formatted and exchanged between a client and a server over a network.

  • Although originally designed for transferring hypertext documents (HTML), HTTP is now used to transfer many types of data, including JSON, XML, images, audio, video, and files.

  • HTTP is a stateless protocol: each request is independent and contains all information required to process it.

  • HTTP runs over TCP. HTTPS runs over TLS over TCP.

TCP = Transmission Control Protocol

  • TCP is a transport-layer protocol that provides reliable, ordered, and error-checked delivery of data between two hosts.

  • TCP establishes a connection between the client and the server before exchanging data.

TLS = Transport Layer Security

  • TLS is a cryptographic protocol that provides secure communication over a network.

HTTP vs HTTPS

  • HTTP sends data in plain text: HTTP (Application Layer) → TCP (Transport Layer)

  • HTTPS is simply HTTP running over a TLS-encrypted TCP connection: HTTP (Application Layer) → TLS → TCP (Transport Layer)

Layers and Protocols

Layer Protocols
Application HTTP, HTTPS, PostgreSQL protocol, Redis protocol
Security TLS
Transport TCP
Internet IP

HTTP Methods (Verbs)

  • GET: Retrieve a resource

  • POST: Create a new resource, or trigger an action or operation (e.g., login, logout, checkout, send-email, calculate)

  • PUT: Completely replace a resource

  • PATCH: Partially update a resource

  • DELETE: Delete a resource

Examples:

  • GET /users: Retrieve all users

  • GET /users/25: Retrieve the user with ID 25

  • If the user with ID 25 was not found, the server returns 404 Not Found

  • GET /users?sort=country: Retrieve all users, sorted by country

  • GET /users?offset=10&size=10: Retrieve users in places 10 through 19

  • GET /users?sort=country&offset=10&size=10: Retrieve all users, sorted by country, in places 10 through 19

  • POST /users name="AxVa" country="DE": Create a new user

  • PUT /users/25 name="ax-va" country="DE": Completely replace the user with ID 25

  • PATCH /uses/25 country="US": Partially update the user with ID 25

  • DELETE /users/25: Delete the user with ID 25

Software Design

Three-Tier Model

  • Presentation Layer / Web Layer (FastAPI)

  • Business Logic Layer / Service Layer

  • Data Layer (SQLAlchemy or SQLModel) ↔ Database (PostgreSQL)

Server Gateway Interface

WSGI = Web Server Gateway Interface

  • Python standard for communication between web servers and web applications

  • Synchronous (one request per worker at a time)

  • Used by frameworks such as Django and Flask

  • Example: Browser ↔ Nginx (web server) ↔ Gunicorn (WSGI server) ↔ Flask App (web application)

  • https://wsgi.readthedocs.io/en/latest/

ASGI = Asynchronous Sever Gateway Interface

  • Successor to WSGI

  • Support async / await

  • Handles many concurrent connections efficiently

  • Used by FastAPI and Starlette

  • Example: Browser ↔ Nginx (web server) ↔ Uvicorn (ASGI server) ↔ FastAPI App (web application)

  • https://asgi.readthedocs.io/en/latest/

Patterns

Dependency Injection

  • Dependency Injection (DI) is a design pattern in which an object or function receives its dependencies from the outside instead of creating them itself.

  • Dependencies are injected, not instantiated.

  • FastAPI implements DI with Depends(): dependency functions are called automatically when your path operation function is called, and they (dependency functions) return values are injected into your path operation function. Depends() only tells FastAPI where that value comes from.

  • FastAPI's dependency injection is more than just passing objects. Dependency functions benefit from the same automatic validation, type conversion, and documentation generation as path operation functions.

  • In FastAPI, there are dependencies on three levels: endpoint, router, and application.

Repository Pattern

  • The Repository Pattern is a design pattern that provides a layer between the business logic (service layer) and the data source.

  • The repository encapsulates all data access logic and hides implementation details of the underlying storage (e.g., SQLite, PostgreSQL, REST API, file system).

  • The service works only with the repository interface and does not know where or how the data is stored.

  • Benefits:

    • Separation of concerns.
    • Easier testing: the repository can be replaced with a fake and a mock.
    • The data source can be changed without modifying the business logic.
    • Works well with Dependency Injection.
  • Example:

    Data Layer: Repository
      |-- SQLite
      |-- PostgreSQL
      |-- In-memory
      |-- Fake Repository
    

Data Layer

DB-API

Python DB-API is a standard interface between Python code and relational database drivers.

It defines common objects and methods such as:

  • connection
  • cursor
  • execute()
  • fetchone(): Returns one tuple or None
  • fetchall(): Returns a sequence of tuples
  • fetchmany(num): Returns up to num tuples
  • commit()
  • rollback()
  • close()

DB-API does not standardize placeholder syntax. Each database driver defines its own parameter style (qmark: ?; format: %s; numeric: :<tuple_index>; named: :<dict_key>; pyformat: %(<dict_key>)s), but all support parameterized queries. Never insert user input into SQL using f-strings, %-string formatting, or string concatenation. Always use parameterized queries. SQL and parameter values are sent separately, so user input is treated as data rather than executable SQL code, preventing SQL injection.

Example:

import psycopg

conn = psycopg.connect(f"dbname=...")
cursor = conn.cursor()

cursor.execute(
  "SELECT * FROM creatures WHERE id = %s", 
  (creature_id,),
)
row = cursor.fetchone()

cursor.execute(
  "UPDATE creatures SET country = %s WHERE id = %s", 
  ("Germany", 1),
)
conn.commit()

cursor.close()
conn.close()

Database drivers like sqlite3 (SQLite), psycopg (PostgreSQL), PyMySQL (MySQL), and mariadb (MariaDB) implement DB-API.

SQLAlchemy works on top of DB-API drivers.

Python Relational Database Libraries

  • SQLAlchemy is the de facto standard for working with relational databases in Python.

  • SQLModel is a higher-level library build on top of SQLAlchemy and Pydantic by the creator of FastAPI.

    • It reduces boilerplate by combining ORM models with data validation, making it especially convenient for small and medium-sized FastAPI projects.

    • Although very elegant, SQLModel is less widely adopted than SQLAlchemy because SQLAlchemy 2.x has become much simpler while continuing to evolve more rapidly.

  • Database drivers provide the low-level communication between Python and a specific database system and usually accessed indirectly through SQLALchemy or another database library.

  • Alembic is a standard database migration tool for SQLAlchemy.

    • It manages schema changes over time by generating and applying versioned migration scripts.

    • Alembic makes it possible to evolve a database safely without manually editing production schemas.

    • It has become the de facto standard for schema migrations in Python projects that use SQLAlchemy.

Authentication and Authorization

Authentication: Who Are You?

  • Authentication is the process of verifying a user's identity.

  • A user provides credentials (such as a username and password), and the server verifies that they are valid.

  • If the verification succeeds, the user is considered authenticated.

  • Authentication verifies a user's identity before granting access to protected resources or allowing restricted actions.

Examples:

  • Username and password: The user proves their identity by providing a username and password.

  • API key: The client authenticates by sending a unique secret key with each request.

  • JWT = JSON Web Token: A signed token format.

    • The JWT format consists of three parts: a header, a payload, and a signature.

    • The header contains metadata, such as the signing algorithm (alg) and token type (typ):

      {
        "alg": "HS256",
        "typ": "JWT"
      }
    • The payload contains claims. The claims describe the authenticated user or client inside a JWT payload, such as their identifier (sub), expiration time (exp), issuer (iss) or application-specific data like roles and permissions:

      {
        "sub": "alice",
        "role": "admin",
        "exp": 1751914800
      } 
    • The signature verifies that the token has not been modified and was signed by a trusted issuer.

    • The header and payload are Base64URL-encoded. A signature is then generated from them using the selected algorithm and a secret key. Finally, the there parts are joined with dots (header.payload.signature) to form the JWT, which is sent in the Authorization: Bearer <token> header.

    • JWT is typically issued after authentication and sent with subsequent requests instead of the user's credentials.

    • JWT is not part of OAuth2, but it is a widely used token format for OAuth2 access tokens.

  • Bearer Token: An HTTP authentication scheme in which the client sends an access token in the Authorization header:

    Authorization: Bearer <token>
    

    The word Bearer means the bearer (holder) of the token. Anyone who presents a valid Bearer token is considered authenticated. A Bearer token is often a JWT, but it can also be an opaque token.

  • OAuth2: OAuth 2.0 (Open Authorization) is an authorization framework.

    • Authentication identifies the user, while OAuth2 defines how access tokens are obtained and used to access protected resources. After a user is authenticated, the server issues an access token instead of requiring the user's credentials for every request.

    • OAuth2 does not define the format of an access token. The token may be a JWT or an opaque token.

    • OAuth2 is widely used for securing REST APIs, delegated authorization with OpenID Connect ("Sign in with Google", "Sign in with GitHub"), or issuing and validating access tokens.

    • OAuth 2.1 is a simplified and more secure revision of OAuth 2.0 that removes deprecated features and follows current security best practices.

Authorization: What Are You Allowed To Do?

  • Authorization is the process of determining what an authenticated user is allowed to do.

  • After authentication, the server checks the user's permissions or roles before allowing access to a resource or operation.

  • If the user does not have sufficient permissions, the server returns 403 Forbidden.

Security

FastAPI Security

https://fastapi.tiangolo.com/tutorial/security/

DDoS = Distributed Denial-of-Service

Distributed Denial-of-Service (DDoS) is an attack in which a large number of compromised computers or devices simultaneously send requests to a server. The goal is to overwhelm the server's resources and make the service unavailable to legitimate users.

Middleware

Middleware is a component that intercepts HTTP requests and responses. It runs before and after an endpoint, allowing cross-cutting functionality such as logging, authentication, cross-origin resource sharing (CORS), compression, and request timing. Unlike FastAPI's dependencies, FastAPI's middleware is typically applied to all requests in the application.

CORS = Cross-Origin Resource Sharing

CORS is browser security mechanism that controls whether a web page from one origin can access resources from another origin. A server enables cross-origin access by sending CORS headers, such as Acces-Control-Allow-Origin. CORS applies only to browsers and does not restrict server-to-server requests or tolls like curl or Postman.

CORS protects the browser, not the API. JWT (or another authentication mechanism) protects the API by proving the client's identity. They solve different security problems and are often used together.

Testing

Monkeypatch

  • Monkeypatch replaces a real object with another real object.

  • Use it when the code should continue working normally, but with a different implementation (e.g., an in-memory SQLite database).

Mocker

  • Mocker replaces an object with a Mock.

  • Use it when you want to isolate the unit under test and verify interactions (assert_called_once_with(), call_count, call_args, etc.).

TestClient

TestClient allows testing a FastAPI application without starting a server or opening a real TCP connection. HTTP requests are sent directly to the ASGI application in memory, making integration tests fast and isolated.

Property-Based Testing

Property-based testing complements traditional testing rather than replacing it.

  • Unit tests verify individual functions and components.

  • Integration/API tests verify specific business scenarios.

  • Property-bases tests automatically explore a large number of input combinations and edge cases.

Unlike conventional tests, property-based testing verifies general properties of a system rather than a small set of manually chosen examples. Test inputs are generated automatically, allowing many different cases to be explored with minimal test code.

  • Hypothesis:

    • Hypothesis ia a property-based testing library for Python. Instead of specifying concrete input values, you define strategies that generate data automatically.

    • Hypothesis executes the test with many different inputs and, if a failure is found, reduces it to the smallest reproducible example (shrinking)

  • Schemathesis:

    • Schemathesis applies property-based testing to REST APIs.

    • It reads the OpenAPI schema generated by FastAPI, automatically generates HTTP requests for all documented endpoints, and validates that responses conform to the API contract.

    • https://schemathesis.readthedocs.io/en/stable/

Hypothesis and Schemathesis understand only the properties or API contract you provide. They do not understand business logic, so they may expose expected behavior if the testes property is formulated too broadly. They are best used alongside conventional integration and API tests.

Run Schemathesis


WARNING: Schemathesis' test cases will change the database

$ st run --header "Authorization:Bearer <admin_token>" http://localhost:8000/openapi.json

Replay a test case

$ st replay <test_case_id>

Load Testing

  • Locust:

    • An open-source load testing tool for web applications and APIs.

    • Simulates many concurrent users and measures response time, throughput, and error rates.

    • Test scenarios are written in Python.

    • https://locust.io/

  • Grasshopper:

    • A lightweight testing framework build on top of Locust.

    • Integrates with pytest and add assertions, performance thresholds, and structured load test reporting.

    • Is useful for organizing and validating load tests in large projects.

    • https://github.com/alteryx/locust-grasshopper

Uvicorn

Uvicorn Workers

  • Running Uvicorn with multiple workers starts multiple independent processes. Each worker has its own event loop and handles requests concurrently.

  • Incoming requests are automatically distributed among workers by the operating system.

  • Workers can run simultaneously on different CPU cores, allowing the application to utilize multiple cores and improve throughput.

  • Workers do not share memory. Global variables, caches, and in-memory data are private to each worker.

  • Shared application state should be stored in external services such as a database, Redis, or a message broker, not in global variables.

Example:

$ uvicorn app.main:app --workers 4

This starts four independent FastAPI application instances, each running in its own process with its own event loop.

Deployment Overview

1. Local Development

During development, the application usually runs directly on the local machine.

Example:

$ uvicorn app.main:app --reload

The application is available at http://localhost:8000. No public IP adress, domain, DNS, or TLS certificate is required.

2. VPS = Virtual Private Server

A Virtual Private Server (VPS) is a virtual machine running on a physical server in a data center.

For the developer, it behaves like a separate Linux computer (e.g., with Docker, Traefik, Uvicorn, database).

A VPS usually provides:

  • virtual CPU cores,
  • RAM,
  • disk storage,
  • root or administrator access,
  • optional public IPv4 and IPv6 addresses.

The hosting provider allocates the public IP address. The application developer does not create an arbitrary public IP address. Some providers treat public addresses as separate resources that must be assigned to the virtual machine.

A FastAPI application can be made available directly through the VPS address, e.g, http://203.0.113.10:8000. However, exposing Uvicorn directly is usually not the preffered production configuration.

3. VPS and Cloud

A VPS can be considered a basic cloud infrastructure service.

With a VPS, the provider gives you a virtual machine, but you generally manage the operating system and software yourself.

The term cloud is broader. It can include:

  • virtual machines,
  • managed databases,
  • object storage,
  • load balancers,
  • container platforms,
  • serverless applications (on cloud-managed infrastructure without requiring developers to manage servers),
  • Kubernetes clusters,
  • monitoring and backup services.

Therefore, a VPS is one type of cloud resource, while a cloud platform provides a wider collection of infrastructures and managed services.

4. Public IP Address

A public IP address identifies a server on the Internet:

Client
  ↓
203.0.113.10
  ↓
 VPS

An application can be accesed directly through this address without DNS. This is sufficient for testing or a simple deployment, but IP addresses are less convenient than domain names.

5. Domain; DNS = Domain Name System

A domain name is a human-readable name such as api.example.com.

A domain is normally registered through a domain registrar and renewed periodically.

Domain Name System (DNS) connects the domain name to the server's IP address:

api.example.com
  ↓ 
 DNS
  ↓ 
203.0.113.10

6. HTTPS and TLS

HTTPS is HTTP protected by TLS.

TLS provides:

  • encryption,
  • server authentication,
  • protection against modification of transmitted data.

To provide HTTPS, the server needs a TLS certificate. The certificate proves that the server is authorized to use a particular domain name or IP address.

7. CA = Certificate Authority: Let's Encrypt

Let's Encrypt is a free, automated, public Certificate Authority (CA).

It issues TLS certificates through the ACME protocol. The certificate-issuance process is separated from normal HTTPS traffic. After the certificate has been issued, Let's Encrypt is no longer involved in each client request. The browser validates the certificate using the trusted CA chain.

For a domain certificate, Let's Encrypt verifies that the requester controls the domain.

A domain is no longer strictly required for every Let's Encrypt certificate. As of 2026, Let's Encrypt also provides publicly trusted certificates for IP addresses. These certificates are short-lived (about six days).

However, a domain remains the usual and more convenient production choice because it provides a stable public name independent of the underlying server address.

8. Traefik

Traefik is an application reverse proxy and load balancer.

(Reverse Proxy represents the server while Forward Proxy represents the client.)

It is an independent infrastructure program written in Go.

Traefik combines three main roles:

  • Application Proxy:

    • Routes HTTP requests to the appropriate application or service.
  • Reverse Proxy:

    • Accepts public HTTP and HTTPS connections;
    • Terminates TLS;
    • Obtains and renews TLS certificates;
    • Redirect HTTP to HTTPS.
  • Load Balancer:

    • Distributes requests among multiple application instancies.

Traefik includes support for separate, automatic certificate management through ACME providers such as Let's Encrypt:

Traefik
  ↓  ACME request
Let's Encrypt
  ↓  certificate
Traefik

After the certificate has been issued:

Browser
  ↓  HTTPS
Traefik

9. TLS Termination

In a common deployment, Traefik handles TLS instead of FastAPI or Uvicorn:

Browser 
  ↓  HTTPS
Traefik 
  ↓  HTTP
Uvicorn and FastAPI

Traefik decrypts the HTTPS connection. This is called TLS termination.

The internal request from Traefik to Uvicorn may use ordinary HTTP when both services communicate through a private network on the same machine or within the same container environment.

As a result, the FastAPI application usually requires no TLS-specific application code.

Proxy headers may still need to be trusted and processed so that FastAPI knows the original client protocol, host, and address.

10. Docker Containers

Traefik and FastAPI commonly run in separate containers:

Docker host
|-- Traefik container
|-- Uvicorn and FastAPI container
|-- PostgreSQL container
|-- Redis container

This separates responsibilities:

  • Traefik handles external networking and HTTPS.
  • FastAPI handles application logic.
  • PostgreSQL stores persistent data.
  • Redis may provide caching or shared state.

Docker Compose normally creates a project network and connects the services to it. Containers on that network can communicate through service names rather than fixed container IP addresses.

11. Load Balancing

Traefik can distribute requests among several FastAPI containers. The application instances should therefore be mostly stateless. Shared data should not be stored only in process-local variables.

Use shared external storage instead:

  • PostgreSQL,
  • Redis,
  • object storage,
  • message brokers.

12. Complete Production Flow

A typical small production deployment looks like this:

https://api.example.com
  ↓  
 DNS
  ↓
Public IP address
  ↓
VPS / cloud virtual machine
  ↓
Traefik container
  ↓ private HTTP
Uvicorn and FastAPI container
  ↓
PostgreSQL container

Certificate management happens separately:

Traefik
  ↓  ACME request
Let's Encrypt
  ↓  certificate
Traefik

Responsibilities:

  • VPS:

    Runs the operating system and application infrastructure

  • Public IP:

    Makes the server reachable over the Internet

  • Domain:

    Provides a stable, readable application name

  • DNS:

    Maps the domain to the public IP address

  • Let's Encrypt:

    Issues trusted TLS certificates

  • Traefik:

    Handles HTTPS, routing, and load balancing

  • Uvicorn:

    Runs the ASGI application

  • FastAPI:

    Implements API and business behavior

  • Database:

    Stores shared persistent data

13. See Also

Links

SQLite Query Optimizer

Celery: Distributed Task Queue

Mojo

Prometheus + FastAPI

Grafana + FastAPI

OpenTelemetry + FastAPI