Skip to main content

Command Palette

Search for a command to run...

Fast API Learning

Updated
14 min readView as Markdown
Fast API Learning
V
Currently transitioning into AI engineering with a strong focus on Python, ML, FastAPI, NLP, Generative AI, and Agentic AI while learning and building in public. Senior Full Stack Engineer with 14+ years of experience building scalable web applications and API-driven systems using Node.js, C#, .NET Core, Web API, AWS, and SQL.

1.API

2.FastAPI

3.FastAPI HTTP Status Codes

4.Pydantic

5.Dependencies

6.Security

1. 🌐 What is an API?

An API (Application Programming Interface) allows different software systems to communicate with each other using requests and responses, usually over HTTP.

1.The request library in python simplifies making http requests to API with methods GET,POST,PUT,DELETE

2.Install requests package using

pip install requests

3.  Need to import requests package in program to request and get response

Request.Get(), request.post() supports sending and retrieving data from server and checking response.status_code to validate request status like success,created,notfound etc.

Example:

APIs are the backbone of web applications, mobile apps, and microservices.


2.⚡ What is FastAPI?

FastAPI is a modern, high‑performance Python framework for building APIs quickly and efficiently.

Why FastAPI? - 🚀 Very fast (built on Starlette & Pydantic) - ✅ Automatic request validation - 📄 Auto‑generated API documentation (Swagger & ReDoc) - 🧠 Easy to learn and developer‑friendly

Install Fast API : pip install fastapi[standard]

Execute Command: “ fastapi  dev ./main.py “ or

“uvicorn filename-wihtoutextention:app --reload

Test/Run with OpenAPI/Swagger:

FastAPI_URL/docs provides Api services, where we can test/execute our services by providing inputs and observing request response

Note: If you use PyCharm as your editor, you can use the Pydantic PyCharm Plugin.

Tiny Example

from fastapi import FastAPI
app = FastAPI()
@app.get("/ping")
def ping():
    return {"message": "API is working"}

🚀 First Steps

Create an app instance and define routes using decorators.

@app.get("/")
async def hello():
    return {"message": "Hello World"}

📌 Path Parameters

Capture values directly from the URL path.

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    return {"item_id": item_id}

🔍 Query Parameters

Parameters passed after ? in the URL.

@app.get("/search/")
async def search(q: str | None = None):
    return {"q": q}

📦 Request Body

Use Pydantic models to receive JSON data.

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

@app.post("/items/")
async def create_item(item: Item):
    return item

🧵 Query String Validation

Add constraints to query parameters.

from fastapi import Query

@app.get("/items/")
async def read_items(q: str | None = Query(None, max_length=50)):
    return {"q": q}

🔢 Path Parameter Validation

Validate numeric path parameters.

from fastapi import Path

@app.get("/users/{uid}")
async def get_user(uid: int = Path(..., gt=0)):
    return {"uid": uid}

🧩 Query Parameter Models

Group query parameters using a model.

from pydantic import BaseModel, Field

class Filter(BaseModel):
    limit: int = Field(10, gt=0)
    sort: str = "asc"

@app.get("/items/")
async def list_items(filters: Filter = Query(...)):
    return filters

🧠 Multiple Body Parameters

Accept multiple request bodies.

class User(BaseModel):
    name: str

@app.put("/update/{id}")
async def update(id: int, item: Item, user: User):
    return {"id": id, "item": item, "user": user}

🏷️ Body Fields Validation

Control validation using Field.

from pydantic import Field

class Product(BaseModel):
    price: float = Field(gt=0)

🌳 Nested Models

Define complex JSON structures.

class Image(BaseModel):
    url: str

class ItemWithImage(BaseModel):
    name: str
    image: Image

Read cookie values in APIs.

from fastapi import Cookie

@app.get("/cookie/")
async def get_cookie(sess: str = Cookie(...)):
    return {"session": sess}

➕ Extra Data Types

FastAPI supports UUID, datetime, Decimal, etc.

from uuid import UUID

@app.get("/uuid/{id}")
async def get_uuid(id: UUID):
    return {"uuid": id}

✅ Summary

FastAPI simplifies API development with strong typing, automatic validation, and excellent performance—making it ideal for modern backend development.

3.📌 FastAPI HTTP Status Codes – Quick Reference


HTTP status codes are used by FastAPI to indicate the result of an API request. They help clients understand whether a request was successful, failed, or requires further action.

FastAPI provides the status module (from fastapi import status) to make status codes more readable and less error-prone.

✅ 2xx – Success Responses

Status Code Name Description
200 OK Request succeeded and response contains data (default for GET).
201 Created Resource successfully created (commonly used in POST).
202 Accepted Request accepted for processing but not completed yet.
204 No Content Request succeeded but returns no response body.

🔁 3xx – Redirection Responses

Status Code Name Description
301 Moved Permanently Resource URL has permanently changed.
302 Found Temporary redirection to another URL.
304 Not Modified Resource not changed since last request (used with caching).

❌ 4xx – Client Error Responses

Status Code Name Description
400 Bad Request Invalid request data or parameters.
401 Unauthorized Authentication required or failed.
403 Forbidden Access denied (no permission).
404 Not Found Requested resource does not exist.
405 Method Not Allowed HTTP method not supported for this endpoint.
409 Conflict Request conflicts with current server state.
422 Unprocessable Entity Validation error (commonly returned by FastAPI).
429 Too Many Requests Rate limit exceeded.

💥 5xx – Server Error Responses

Status Code Name Description
500 Internal Server Error Generic server-side error.
502 Bad Gateway Invalid response from upstream server.
503 Service Unavailable Server temporarily unavailable.
504 Gateway Timeout Upstream server did not respond in time.

🧪 Commonly Used Status Codes in FastAPI

Use Case Recommended Status Code
Successful GET 200 OK
Successful POST 201 Created
Successful DELETE 204 No Content
Invalid input data 422 Unprocessable Entity
Invalid credentials 401 Unauthorized
No access rights 403 Forbidden
Resource missing 404 Not Found

🛠️ Using Status Codes in FastAPI

from fastapi import FastAPI, status

app = FastAPI()

@app.post("/items", status_code=status.HTTP_201_CREATED)
def create_item(item: dict):
    return item

✅ Fast API HTTP Status Code Summary

HTTP status codes make FastAPI APIs clear, predictable, and REST-compliant, helping clients handle responses correctly and improving overall API design.

🧩 Pydantic in FastAPI


Pydantic is a Python library used by FastAPI to perform data validation, parsing, and serialization using Python type hints.

It ensures that incoming data is: -

✅ Type-safe -

✅ Automatically validated -

✅ Converted to correct Python objects -

✅ Clearly documented in Swagger UI

FastAPI deeply integrates Pydantic to handle request bodies, query parameters, path parameters, and application settings

🔢 Pydantic Core Concepts (Short & Practical)

1️⃣ BaseModel

Used to define structured data and validate inputs.

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

2️⃣ Field

Adds validation rules and metadata to model fields.

from pydantic import Field

class Product(BaseModel):
    price: float = Field(gt=0)

3️⃣ BaseSettings

Used for managing configuration via environment variables.

from pydantic import BaseSettings

class Settings(BaseSettings):
    app_name: str

4️⃣ EmailStr, HttpUrl

Built-in types for validating emails and URLs.

from pydantic import EmailStr, HttpUrl

class Contact(BaseModel):
    email: EmailStr
    website: HttpUrl

5️⃣ field_validator

Validates and transforms individual fields.

from pydantic import field_validator

class User(BaseModel):
    name: str

@field_validator("name")
    def format_name(cls, v):
        return v.title()

6️⃣ model_validator

Validates the entire model after field validation.

from pydantic import model_validator

class User(BaseModel):
    age: int

@model_validator(mode="after")
    def check_age(self):
        if self.age < 18:
            raise ValueError("Must be 18+")
        return self

7️⃣ ConfigDict

Controls model behavior such as extra fields and strict mode.

from pydantic import ConfigDict

class User(BaseModel):
    name: str
    model_config = ConfigDict(extra="forbid")

8️⃣ RootModel

Used when the model wraps a single value or list.

from pydantic import RootModel

class Scores(RootModel[list[int]]):
    pass

9️⃣ TypeAdapter

Validates data without defining a full model.

from pydantic import TypeAdapter

ta = TypeAdapter(int)
ta.validate_python("10")

✅ Pydantic Summary

Pydantic is the backbone of FastAPI’s validation system, making APIs reliable, readable, and safe with minimal code.

🔗 Dependencies in FastAPI


What is Dependency Injection?

Dependency Injection (DI) is a design pattern where required components (dependencies) are provided to a function instead of being created inside it.

FastAPI’s dependency system is simple, powerful, and developer‑friendly, making it easy to integrate reusable logic across your application.

✅ When to Use Dependencies

Use dependencies when you need to:

🔁 Share common logic across endpoints

🗄️ Share database connections or sessions

🔐 Enforce authentication, authorization, or role checks

📊 Add logging, monitoring, or rate‑limiting

♻️ Reduce code duplication

📥 Importing Depends

To use dependencies, import Depends from FastAPI:

from fastapi import Depends, FastAPI

🛠️ Function-Based Dependencies

Declare dependencies just like Body, Query, or Path parameters—using Depends() in function arguments.

Example

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(
    q: str | None = None,
    skip: int = 0,
    limit: int = 100
):
    return {"q": q, "skip": skip, "limit": limit}

CommonsDep = Annotated[dict, Depends(common_parameters)]

@app.get("/items")
async def read_items(commons: CommonsDep):
    return commons

@app.get("/users")
async def read_users(commons: CommonsDep):
    return commons

▶️ Run Command

uvicorn sampleFastAPI:app --reload


🔌 FastAPI Compatibility

FastAPI’s dependency system works seamlessly with: - Relational databases (PostgreSQL, MySQL, etc.) - NoSQL databases (MongoDB, Redis, etc.) - External packages - External APIs - Authentication & authorization systems - API usage monitoring tools - Response data injection systems


🧱 Classes as Dependencies

FastAPI also supports class‑based dependencies, not just functions.

When a class is used with Depends, FastAPI: 1. Creates an instance of the class 2. Injects request data into the init() method 3. Passes the instance to the path operation function

This is useful for grouping related parameters or logic together.

Example

from fastapi import Depends, FastAPI

app = FastAPI()

class CommonParams:
    def init(self, q: str | None = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit

@app.get("/items/")
def read_items(params: CommonParams = Depends()):
    return params.__dict__

👍 Why Use Class-Based Dependencies?

·         Cleaner and better‑organized code

·         Easy reuse across multiple endpoints

·         Ideal for growing logic (filters, auth, validation)

·         Simple to extend later

📌 When to Prefer Them

·         Shared query parameters

·         Pagination and filtering logic

·         Authentication or authorization helpers

🌍 Global Dependencies

Sometimes you want dependencies to apply to all endpoints in the application.

Example

from typing import Annotated
from fastapi import Depends, FastAPI, Header, HTTPException

async def verify_token(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")

async def verify_key(x_key: Annotated[str, Header()]):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key

app = FastAPI(
    dependencies=[Depends(verify_token), Depends(verify_key)]
)

@app.get("/items/")
async def read_items():
    return [{"item": "Portal Gun"}, {"item": "Plumbus"}]

@app.get("/users/")
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]

🔄 Dependencies with yield

FastAPI supports dependencies that need cleanup logic after request processing.

Use yield instead of return, and write cleanup code after it.

⚠️ Important: Use yield only once per dependency.

Example

from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException

app = FastAPI()

DATA = {
    "plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
    "portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}

class OwnerError(Exception):
    pass

def get_username():
    try:
        yield "Rick"
    except OwnerError as e:
        raise HTTPException(
            status_code=400,
            detail=f"Owner error: {e}"
        )

@app.get("/items/{item_id}")
def get_item(
    item_id: str,
    username: Annotated[str, Depends(get_username)]
):
    if item_id not in DATA:
        raise HTTPException(status_code=404, detail="Item not found")
    item = DATA[item_id]
    if item["owner"] != username:
        raise OwnerError(username)
    return item

Code Dependencies:

✅ Dependencies Summary

FastAPI dependencies help you write clean, reusable, and maintainable code by separating shared logic from business logic. They scale naturally from simple helpers to complex application-wide concerns.

5.🔐 FastAPI Security


Security in APIs generally involves authentication (who you are) and authorization (what you can access). FastAPI supports modern, standard-based security mechanisms that integrate cleanly with OpenAPI and Swagger UI.

🔑 Common Security Standards

OAuth2

OAuth2 is a widely used authorization framework. - Supports complex use cases - Allows third‑party login (Google, Facebook, GitHub, etc.) - Requires HTTPS in production

OAuth1

·         Older and more complex

·         Defines how to encrypt communication

·         Rarely used in modern applications

OpenID Connect

·         Built on top of OAuth2

·         Clarifies ambiguous OAuth2 areas

·         Used by providers like Google Login

📄 OpenAPI & FastAPI Security

FastAPI is built on the OpenAPI specification, which automatically documents API security.

With OpenAPI, FastAPI can: - Define security schemes (OAuth2, JWT Bearer, API Keys, HTTP Basic) - Describe authentication requirements clearly - Generate interactive Swagger UI with an Authorize button

Benefits

·         Self‑documented security

·         Easier frontend integration

·         Fewer authentication misconfigurations🛠️ FastAPI Security Utilities

FastAPI provides built‑in tools inside the fastapi.security module to simplify security implementation.

Common utilities include: - OAuth2PasswordBearer - OAuth2PasswordRequestForm - HTTP Basic authentication - API key support

🔐 OAuth2 in FastAPI – Step by Step (Concise)

FastAPI mainly demonstrates security using the OAuth2 Password Flow, which works well for first‑party applications.

1️⃣ OAuth2 Password Flow – Basics

·         Client sends username & password to /token

·         Server returns an access token

·         Client sends token in Authorization: Bearer header

·         Protected endpoints validate the token

Swagger UI automatically provides an Authorize dialog.

2️⃣ Simple OAuth2 (Password + Bearer Token)

This example demonstrates OAuth2 mechanics using a fake token: - Credentials are extracted using OAuth2PasswordRequestForm - Response contains access_token and token_type - Focuses on understanding OAuth2 flow, not real security

3️⃣ OAuth2 with JWT & Password Hashing (Real‑World)

This approach adds production‑ready security: - Passwords are securely hashed (Argon2) - Tokens are JWTs (JSON Web Tokens) - JWTs contain user identity and expiry - Tokens are cryptographically signed

JWT allows stateless authentication, meaning the server doesn’t store sessions.

📦 Required Packages

Install required libraries:

pip install pyjwt
pip install pwdlib[argon2]

🧪 OAuth2 + JWT Complete Example

from datetime import datetime, timedelta, timezone
from typing import Annotated
import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from pwdlib import PasswordHash
from pydantic import BaseModel

# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "\(argon2id\)v=19\(m=65536,t=3,p=4\)wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
        "disabled": False,
    }
}

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: str | None = None

class User(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = None

class UserInDB(User):
    hashed_password: str

password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

app = FastAPI()

def verify_password(plain_password, hashed_password):
    return password_hash.verify(plain_password, hashed_password)

def get_password_hash(password):
    return password_hash.hash(password)

def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except InvalidTokenError:
        raise credentials_exception
    user = get_user(fake_users_db, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user


async def get_current_active_user(
    current_user: Annotated[User, Depends(get_current_user)],
):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user


@app.post("/token")
async def login_for_access_token(
    form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
    user = authenticate_user(fake_users_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return Token(access_token=access_token, token_type="bearer")


@app.get("/users/me/", response_model=User)
async def read_users_me(
    current_user: Annotated[User, Depends(get_current_active_user)],
):
    return current_user

@app.get("/users/me/items/")
async def read_own_items(
    current_user: Annotated[User, Depends(get_current_active_user)],
):
    return [{"item_id": "Foo", "owner": current_user.username}]

✅ Fast API Security Summary

FastAPI security is built on open standards like OAuth2, JWT, and OpenAPI. With minimal code, you get secure authentication, automatic documentation, and production‑ready patterns.

More from this blog