Skip to content

vanHeemstraSystems/learning-idp-api-development

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 

Repository files navigation

Learning IDP: API Development

This repository focuses on mastering API development using Python frameworks to build, manage, and automate RESTful APIs for Internal Development Platform (IDP) development.

🎯 Learning Objectives

By working through this repository, you will:

  1. Master FastAPI and Flask for API development
  2. Implement RESTful API best practices
  3. Work with API authentication and authorization
  4. Build GraphQL APIs with Python
  5. Implement API documentation with OpenAPI/Swagger
  6. Configure API gateways and rate limiting
  7. Deploy APIs to production environments

πŸ“š Prerequisites

πŸ—‚οΈ Directory Structure

learning-idp-api-development/
β”œβ”€β”€ README.md                          # This file
β”œβ”€β”€ REFERENCES.md                      # Links to resources and related repos
β”œβ”€β”€ pyproject.toml                     # Python project configuration
β”œβ”€β”€ requirements.txt                   # Python dependencies
β”œβ”€β”€ requirements-dev.txt               # Development dependencies
β”œβ”€β”€ .python-version                    # Python version for pyenv
β”œβ”€β”€ .gitignore                         # Git ignore patterns
β”œβ”€β”€ .env.example                       # Environment variables template
β”‚
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ concepts/
β”‚   β”‚   β”œβ”€β”€ 01-api-overview.md
β”‚   β”‚   β”œβ”€β”€ 02-rest-principles.md
β”‚   β”‚   β”œβ”€β”€ 03-api-design.md
β”‚   β”‚   β”œβ”€β”€ 04-authentication.md
β”‚   β”‚   β”œβ”€β”€ 05-api-versioning.md
β”‚   β”‚   └── 06-api-security.md
β”‚   β”œβ”€β”€ guides/
β”‚   β”‚   β”œβ”€β”€ getting-started.md
β”‚   β”‚   β”œβ”€β”€ fastapi-setup.md
β”‚   β”‚   β”œβ”€β”€ authentication-oauth2.md
β”‚   β”‚   β”œβ”€β”€ api-documentation.md
β”‚   β”‚   └── deployment.md
β”‚   └── examples/
β”‚       β”œβ”€β”€ simple-rest-api.md
β”‚       β”œβ”€β”€ crud-operations.md
β”‚       β”œβ”€β”€ authentication-flow.md
β”‚       β”œβ”€β”€ graphql-api.md
β”‚       └── microservices-api.md
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚
β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ config.py                  # Configuration management
β”‚   β”‚   β”œβ”€β”€ exceptions.py              # Custom exceptions
β”‚   β”‚   β”œβ”€β”€ logging_config.py          # Logging setup
β”‚   β”‚   └── dependencies.py            # FastAPI dependencies
β”‚   β”‚
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ main.py                    # FastAPI application
β”‚   β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”‚   β”œβ”€β”€ health.py              # Health check endpoint
β”‚   β”‚   β”‚   β”œβ”€β”€ users.py               # User endpoints
β”‚   β”‚   β”‚   β”œβ”€β”€ auth.py                # Authentication endpoints
β”‚   β”‚   β”‚   └── items.py               # Resource endpoints
β”‚   β”‚   └── middleware/
β”‚   β”‚       β”œβ”€β”€ __init__.py
β”‚   β”‚       β”œβ”€β”€ cors.py                # CORS middleware
β”‚   β”‚       β”œβ”€β”€ auth.py                # Auth middleware
β”‚   β”‚       β”œβ”€β”€ rate_limit.py          # Rate limiting
β”‚   β”‚       └── logging.py             # Request logging
β”‚   β”‚
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ user.py                    # User model
β”‚   β”‚   β”œβ”€β”€ item.py                    # Item model
β”‚   β”‚   β”œβ”€β”€ auth.py                    # Auth models
β”‚   β”‚   └── base.py                    # Base model
β”‚   β”‚
β”‚   β”œβ”€β”€ schemas/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ user.py                    # User schemas (Pydantic)
β”‚   β”‚   β”œβ”€β”€ item.py                    # Item schemas
β”‚   β”‚   β”œβ”€β”€ auth.py                    # Auth schemas
β”‚   β”‚   └── common.py                  # Common schemas
β”‚   β”‚
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ user_service.py            # User business logic
β”‚   β”‚   β”œβ”€β”€ auth_service.py            # Authentication logic
β”‚   β”‚   β”œβ”€β”€ item_service.py            # Item business logic
β”‚   β”‚   └── cache_service.py           # Caching service
β”‚   β”‚
β”‚   β”œβ”€β”€ repositories/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ user_repository.py         # User data access
β”‚   β”‚   β”œβ”€β”€ item_repository.py         # Item data access
β”‚   β”‚   └── base_repository.py         # Base repository
β”‚   β”‚
β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ jwt.py                     # JWT token handling
β”‚   β”‚   β”œβ”€β”€ oauth2.py                  # OAuth2 implementation
β”‚   β”‚   β”œβ”€β”€ password.py                # Password hashing
β”‚   β”‚   └── permissions.py             # Permission system
β”‚   β”‚
β”‚   β”œβ”€β”€ database/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ session.py                 # Database session
β”‚   β”‚   β”œβ”€β”€ base.py                    # Base database class
β”‚   β”‚   └── migrations/                # Alembic migrations
β”‚   β”‚       └── versions/
β”‚   β”‚
β”‚   β”œβ”€β”€ graphql/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ schema.py                  # GraphQL schema
β”‚   β”‚   β”œβ”€β”€ queries.py                 # GraphQL queries
β”‚   β”‚   β”œβ”€β”€ mutations.py               # GraphQL mutations
β”‚   β”‚   └── resolvers.py               # Field resolvers
β”‚   β”‚
β”‚   └── utils/
β”‚       β”œβ”€β”€ __init__.py
β”‚       β”œβ”€β”€ validators.py              # Input validators
β”‚       β”œβ”€β”€ serializers.py             # Data serializers
β”‚       β”œβ”€β”€ pagination.py              # Pagination helpers
β”‚       └── rate_limiter.py            # Rate limiting utilities
β”‚
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ 01_fastapi_basics/
β”‚   β”‚   β”œβ”€β”€ 01_hello_world.py
β”‚   β”‚   β”œβ”€β”€ 02_path_parameters.py
β”‚   β”‚   β”œβ”€β”€ 03_query_parameters.py
β”‚   β”‚   β”œβ”€β”€ 04_request_body.py
β”‚   β”‚   └── 05_response_model.py
β”‚   β”‚
β”‚   β”œβ”€β”€ 02_crud_operations/
β”‚   β”‚   β”œβ”€β”€ 01_create_user.py
β”‚   β”‚   β”œβ”€β”€ 02_read_user.py
β”‚   β”‚   β”œβ”€β”€ 03_update_user.py
β”‚   β”‚   β”œβ”€β”€ 04_delete_user.py
β”‚   β”‚   └── 05_list_users.py
β”‚   β”‚
β”‚   β”œβ”€β”€ 03_authentication/
β”‚   β”‚   β”œβ”€β”€ 01_basic_auth.py
β”‚   β”‚   β”œβ”€β”€ 02_jwt_auth.py
β”‚   β”‚   β”œβ”€β”€ 03_oauth2_flow.py
β”‚   β”‚   β”œβ”€β”€ 04_refresh_tokens.py
β”‚   β”‚   └── 05_permissions.py
β”‚   β”‚
β”‚   β”œβ”€β”€ 04_database_integration/
β”‚   β”‚   β”œβ”€β”€ 01_sqlalchemy_setup.py
β”‚   β”‚   β”œβ”€β”€ 02_models.py
β”‚   β”‚   β”œβ”€β”€ 03_relationships.py
β”‚   β”‚   β”œβ”€β”€ 04_queries.py
β”‚   β”‚   └── 05_migrations.py
β”‚   β”‚
β”‚   β”œβ”€β”€ 05_advanced_features/
β”‚   β”‚   β”œβ”€β”€ 01_file_upload.py
β”‚   β”‚   β”œβ”€β”€ 02_background_tasks.py
β”‚   β”‚   β”œβ”€β”€ 03_websockets.py
β”‚   β”‚   β”œβ”€β”€ 04_streaming.py
β”‚   β”‚   └── 05_caching.py
β”‚   β”‚
β”‚   β”œβ”€β”€ 06_graphql/
β”‚   β”‚   β”œβ”€β”€ 01_basic_schema.py
β”‚   β”‚   β”œβ”€β”€ 02_queries.py
β”‚   β”‚   β”œβ”€β”€ 03_mutations.py
β”‚   β”‚   β”œβ”€β”€ 04_subscriptions.py
β”‚   β”‚   └── 05_dataloader.py
β”‚   β”‚
β”‚   β”œβ”€β”€ 07_testing/
β”‚   β”‚   β”œβ”€β”€ 01_test_endpoints.py
β”‚   β”‚   β”œβ”€β”€ 02_test_auth.py
β”‚   β”‚   β”œβ”€β”€ 03_test_database.py
β”‚   β”‚   β”œβ”€β”€ 04_test_integration.py
β”‚   β”‚   └── 05_test_performance.py
β”‚   β”‚
β”‚   └── 08_deployment/
β”‚       β”œβ”€β”€ 01_docker_deployment/
β”‚       β”‚   β”œβ”€β”€ Dockerfile
β”‚       β”‚   └── docker-compose.yml
β”‚       β”œβ”€β”€ 02_kubernetes_deployment/
β”‚       β”‚   β”œβ”€β”€ deployment.yaml
β”‚       β”‚   └── service.yaml
β”‚       └── 03_azure_deployment/
β”‚           └── deploy_to_azure.py
β”‚
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ basic_rest_api.py
β”‚   β”‚   β”œβ”€β”€ crud_api.py
β”‚   β”‚   └── microservice_api.py
β”‚   β”œβ”€β”€ schemas/
β”‚   β”‚   β”œβ”€β”€ user_schema.py
β”‚   β”‚   β”œβ”€β”€ auth_schema.py
β”‚   β”‚   └── error_schema.py
β”‚   └── middleware/
β”‚       β”œβ”€β”€ auth_middleware.py
β”‚       β”œβ”€β”€ cors_middleware.py
β”‚       └── rate_limit_middleware.py
β”‚
β”œβ”€β”€ notebooks/
β”‚   β”œβ”€β”€ 01_api_basics.ipynb
β”‚   β”œβ”€β”€ 02_authentication.ipynb
β”‚   β”œβ”€β”€ 03_database_integration.ipynb
β”‚   β”œβ”€β”€ 04_graphql_api.ipynb
β”‚   └── 05_api_testing.ipynb
β”‚
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ create_api_project.sh          # Project setup script
β”‚   β”œβ”€β”€ run_dev_server.sh              # Development server
β”‚   β”œβ”€β”€ generate_openapi.py            # Generate OpenAPI spec
β”‚   └── migrate_database.sh            # Run migrations
β”‚
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ conftest.py
β”‚   β”œβ”€β”€ unit/
β”‚   β”‚   β”œβ”€β”€ test_models.py
β”‚   β”‚   β”œβ”€β”€ test_schemas.py
β”‚   β”‚   β”œβ”€β”€ test_services.py
β”‚   β”‚   └── test_auth.py
β”‚   β”œβ”€β”€ integration/
β”‚   β”‚   β”œβ”€β”€ test_api_endpoints.py
β”‚   β”‚   β”œβ”€β”€ test_authentication.py
β”‚   β”‚   β”œβ”€β”€ test_database.py
β”‚   β”‚   └── test_webhooks.py
β”‚   └── e2e/
β”‚       β”œβ”€β”€ test_user_flow.py
β”‚       └── test_api_flow.py
β”‚
└── .github/
    └── workflows/
        β”œβ”€β”€ api-test.yml               # API testing
        β”œβ”€β”€ api-deploy.yml             # Deployment
        └── api-docs.yml               # Documentation generation

πŸš€ Getting Started

1. Clone the Repository

git clone https://github.com/vanHeemstraSystems/learning-idp-api-development.git
cd learning-idp-api-development

2. Set Up Python Environment

# Create virtual environment
python3 -m venv venv

# Activate virtual environment
# On Linux/MacOS:
source venv/bin/activate
# On Windows:
# venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt

3. Run Your First API

# Run the basic FastAPI example
cd examples/01_fastapi_basics
uvicorn 01_hello_world:app --reload

# Test it
curl http://localhost:8000
curl http://localhost:8000/docs  # Swagger UI

4. Create a Full CRUD API

# Run the CRUD example
python examples/02_crud_operations/01_create_user.py

# Access the API
curl -X POST http://localhost:8000/users \
  -H "Content-Type: application/json" \
  -d '{"name": "John Doe", "email": "[email protected]"}'

πŸ“– Learning Path

Follow this recommended sequence:

Week 1: FastAPI Fundamentals

Day 1-2: FastAPI Basics

  1. Read docs/concepts/01-api-overview.md
  2. Complete examples in examples/01_fastapi_basics/
  3. Practice path and query parameters

Day 3-4: CRUD Operations

  1. Study docs/concepts/02-rest-principles.md
  2. Work through examples/02_crud_operations/
  3. Implement full CRUD API

Day 5-7: Database Integration

  1. Complete examples in examples/04_database_integration/
  2. Implement SQLAlchemy models
  3. Practice database migrations

Week 2: Authentication & Security

Day 1-3: Authentication

  1. Read docs/concepts/04-authentication.md
  2. Complete examples in examples/03_authentication/
  3. Implement JWT authentication

Day 4-7: API Security

  1. Study docs/concepts/06-api-security.md
  2. Implement OAuth2 flows
  3. Configure CORS and rate limiting

Week 3: Advanced Features

Day 1-3: Advanced API Features

  1. Work through examples/05_advanced_features/
  2. Implement file uploads
  3. Configure background tasks

Day 4-7: GraphQL API

  1. Complete examples in examples/06_graphql/
  2. Build GraphQL schema
  3. Implement queries and mutations

Week 4: Testing & Deployment

Day 1-3: API Testing

  1. Study testing best practices
  2. Work through examples/07_testing/
  3. Implement integration tests

Day 4-7: Deployment

  1. Complete examples in examples/08_deployment/
  2. Deploy to containers
  3. Deploy to Azure

πŸ”‘ Key Python Packages

API Frameworks

# FastAPI Stack
fastapi>=0.109.0            # Modern API framework
uvicorn[standard]>=0.27.0   # ASGI server
pydantic>=2.5.0             # Data validation
python-multipart>=0.0.6     # Form data support

# Flask Alternative
flask>=3.0.0                # Flask framework
flask-restful>=0.3.10       # REST extensions
flask-smorest>=0.44.0       # OpenAPI integration

# GraphQL
strawberry-graphql>=0.219.0 # GraphQL library
graphene>=3.3              # GraphQL framework

Database & ORM

sqlalchemy>=2.0.0           # SQL toolkit and ORM
alembic>=1.13.0             # Database migrations
asyncpg>=0.29.0             # Async PostgreSQL
databases>=0.8.0            # Async database support

Authentication

python-jose[cryptography]>=3.3.0  # JWT tokens
passlib[bcrypt]>=1.7.4      # Password hashing
python-oauth2>=1.1.1        # OAuth2 implementation

πŸ’‘ Common Operations Examples

Basic FastAPI Application

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
from typing import List

app = FastAPI(
    title="My API",
    description="API for learning",
    version="1.0.0"
)

# Pydantic models
class User(BaseModel):
    id: int | None = None
    name: str
    email: EmailStr
    is_active: bool = True

# In-memory storage
users_db: List[User] = []

@app.get("/")
async def root():
    return {"message": "Welcome to my API"}

@app.post("/users", response_model=User, status_code=201)
async def create_user(user: User):
    user.id = len(users_db) + 1
    users_db.append(user)
    return user

@app.get("/users", response_model=List[User])
async def list_users():
    return users_db

@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
    for user in users_db:
        if user.id == user_id:
            return user
    raise HTTPException(status_code=404, detail="User not found")

@app.put("/users/{user_id}", response_model=User)
async def update_user(user_id: int, updated_user: User):
    for idx, user in enumerate(users_db):
        if user.id == user_id:
            updated_user.id = user_id
            users_db[idx] = updated_user
            return updated_user
    raise HTTPException(status_code=404, detail="User not found")

@app.delete("/users/{user_id}", status_code=204)
async def delete_user(user_id: int):
    for idx, user in enumerate(users_db):
        if user.id == user_id:
            users_db.pop(idx)
            return
    raise HTTPException(status_code=404, detail="User not found")

JWT Authentication

from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

# Configuration
SECRET_KEY = "your-secret-key-here"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Password utilities
def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    return pwd_context.hash(password)

# Token utilities
def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + 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: 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: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    # Get user from database
    user = get_user_from_db(username)
    if user is None:
        raise credentials_exception
    
    return user

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(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 {"access_token": access_token, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

Database Integration with SQLAlchemy

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from fastapi import Depends

# Database setup
DATABASE_URL = "postgresql://user:password@localhost/dbname"

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# Database models
class UserDB(Base):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    email = Column(String, unique=True, index=True)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)

# Create tables
Base.metadata.create_all(bind=engine)

# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# API endpoints with database
@app.post("/users", response_model=User)
async def create_user_db(user: UserCreate, db: Session = Depends(get_db)):
    db_user = UserDB(
        name=user.name,
        email=user.email,
        hashed_password=get_password_hash(user.password)
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

@app.get("/users/{user_id}", response_model=User)
async def get_user_db(user_id: int, db: Session = Depends(get_db)):
    db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user

GraphQL API with Strawberry

import strawberry
from typing import List
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

@strawberry.type
class User:
    id: int
    name: str
    email: str
    is_active: bool

@strawberry.type
class Query:
    @strawberry.field
    def user(self, id: int) -> User | None:
        # Get user from database
        return get_user_from_db(id)
    
    @strawberry.field
    def users(self) -> List[User]:
        # Get all users from database
        return get_all_users_from_db()

@strawberry.type
class Mutation:
    @strawberry.mutation
    def create_user(self, name: str, email: str) -> User:
        # Create user in database
        return create_user_in_db(name, email)
    
    @strawberry.mutation
    def update_user(self, id: int, name: str | None = None, 
                    email: str | None = None) -> User:
        # Update user in database
        return update_user_in_db(id, name, email)

schema = strawberry.Schema(query=Query, mutation=Mutation)

# Add to FastAPI
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")

Rate Limiting

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

@app.get("/limited")
@limiter.limit("5/minute")
async def limited_endpoint(request: Request):
    return {"message": "This endpoint is rate limited"}

🎯 Best Practices

1. API Versioning

from fastapi import APIRouter

# Version 1
router_v1 = APIRouter(prefix="/api/v1")

@router_v1.get("/users")
async def get_users_v1():
    return {"version": "1.0", "users": []}

# Version 2
router_v2 = APIRouter(prefix="/api/v2")

@router_v2.get("/users")
async def get_users_v2():
    return {"version": "2.0", "users": [], "metadata": {}}

app.include_router(router_v1)
app.include_router(router_v2)

2. Error Handling

from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse

class APIError(Exception):
    def __init__(self, status_code: int, detail: str):
        self.status_code = status_code
        self.detail = detail

@app.exception_handler(APIError)
async def api_error_handler(request: Request, exc: APIError):
    return JSONResponse(
        status_code=exc.status_code,
        content={"error": exc.detail}
    )

3. Request Validation

from pydantic import BaseModel, validator, EmailStr

class UserCreate(BaseModel):
    name: str
    email: EmailStr
    password: str
    
    @validator('name')
    def name_must_not_be_empty(cls, v):
        if not v.strip():
            raise ValueError('name cannot be empty')
        return v
    
    @validator('password')
    def password_strength(cls, v):
        if len(v) < 8:
            raise ValueError('password must be at least 8 characters')
        return v

4. Response Models

from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str
    email: str
    
    class Config:
        from_attributes = True  # For SQLAlchemy models

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    return get_user_from_db(user_id)

πŸ”— Related Repositories

🀝 Contributing

This is a personal learning repository, but suggestions and improvements are welcome!

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure all tests pass
  5. Submit a pull request

πŸ“„ License

This project is for educational purposes. See LICENSE file for details.

πŸ“§ Contact

Willem van Heemstra


Last updated: December 18, 2025 Part of the learning-internal-development-platform series

About

Learning IDP API Development

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published