Python Quick Start

Get started with Bytedocs in Python applications

Bytedocs for Python supports popular frameworks with automatic Pydantic model detection and type hints analysis.

Supported Frameworks

  • FastAPI - Modern, fast web framework with automatic API docs
  • Flask - Micro web framework
  • Django - Full-featured web framework

Installation

Choose your framework:

# FastAPI
pip install bytedocs-fastapi

# Flask
pip install bytedocs-flask

# Django
pip install bytedocs-django

Quick Start Examples

FastAPI

from fastapi import FastAPI
from bytedocs_fastapi import setup_bytedocs

# IMPORTANT: Disable FastAPI's built-in Swagger
app = FastAPI(
    docs_url=None,      # Disable default Swagger UI
    redoc_url=None,     # Disable default ReDoc
    openapi_url=None    # Disable default OpenAPI
)

# Setup Bytedocs - One line!
setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
    "description": "My awesome API"
})

# Define your routes as normal
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id, "name": "John Doe"}

@app.post("/users")
async def create_user(name: str, email: str):
    return {"id": 1, "name": name, "email": email}

FastAPI with Pydantic Models

from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from bytedocs_fastapi import setup_bytedocs

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)

setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
})

# Pydantic models are automatically documented
class User(BaseModel):
    id: int = Field(..., description="User ID", example=123)
    name: str = Field(..., description="User's full name", example="John Doe")
    email: EmailStr = Field(..., description="Email address", example="john@example.com")
    age: int | None = Field(None, ge=0, le=150, description="User's age")

class CreateUserRequest(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    email: EmailStr
    age: int | None = Field(None, ge=0, le=150)

class UserResponse(BaseModel):
    id: int
    name: str
    email: str
    created_at: str

@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
    return User(
        id=user_id,
        name="John Doe",
        email="john@example.com",
        age=30
    )

@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: CreateUserRequest):
    return UserResponse(
        id=1,
        name=user.name,
        email=user.email,
        created_at="2025-01-01T00:00:00Z"
    )

Flask

from flask import Flask, request, jsonify
from bytedocs_flask import setup_bytedocs

app = Flask(__name__)

# Setup Bytedocs
setup_bytedocs(app, {
    "title": "My Flask API",
    "version": "1.0.0",
})

@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    """
    Get user by ID
    ---
    parameters:
      - name: user_id
        in: path
        type: integer
        required: true
        description: User ID
    responses:
      200:
        description: User object
        schema:
          type: object
          properties:
            id:
              type: integer
            name:
              type: string
            email:
              type: string
    """
    return jsonify({
        "id": user_id,
        "name": "John Doe",
        "email": "john@example.com"
    })

@app.route('/users', methods=['POST'])
def create_user():
    """
    Create a new user
    ---
    parameters:
      - name: body
        in: body
        required: true
        schema:
          type: object
          required:
            - name
            - email
          properties:
            name:
              type: string
            email:
              type: string
    responses:
      201:
        description: Created user
    """
    data = request.get_json()
    return jsonify({
        "id": 1,
        "name": data['name'],
        "email": data['email']
    }), 201

if __name__ == '__main__':
    app.run(debug=True)

Django

# settings.py
INSTALLED_APPS = [
    # ...
    'bytedocs_django',
]

BYTEDOCS = {
    'TITLE': 'My Django API',
    'VERSION': '1.0.0',
    'DESCRIPTION': 'My awesome Django API',
    'DOCS_PATH': '/docs',
}
# urls.py
from django.urls import path, include
from bytedocs_django import get_docs_urls

urlpatterns = [
    # Your API endpoints
    path('api/users/<int:user_id>/', views.get_user),
    path('api/users/', views.create_user),

    # Bytedocs
    path('', include(get_docs_urls())),
]
# views.py
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
import json

@require_http_methods(["GET"])
def get_user(request, user_id):
    """
    Get user by ID
    """
    return JsonResponse({
        "id": user_id,
        "name": "John Doe",
        "email": "john@example.com"
    })

@require_http_methods(["POST"])
def create_user(request):
    """
    Create a new user
    """
    data = json.loads(request.body)
    return JsonResponse({
        "id": 1,
        "name": data['name'],
        "email": data['email']
    }, status=201)

Configuration Options

Basic Configuration

setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
    "description": "API description",
    "docs_path": "/docs",        # Default: "/docs"
    "auto_detect": True,          # Default: True
})

Multiple Environments

setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
    "base_urls": [
        {"name": "Production", "url": "https://api.example.com"},
        {"name": "Staging", "url": "https://staging.example.com"},
        {"name": "Local", "url": "http://localhost:8000"},
    ],
})

With Authentication

setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
    "auth": {
        "enabled": True,
        "type": "session",
        "username": "admin",
        "password": "secret",
    },
})

With AI Assistant

import os

setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
    "ai": {
        "enabled": True,
        "provider": "openai",
        "api_key": os.getenv("OPENAI_API_KEY"),
        "model": "gpt-4o-mini",
    },
})

Pydantic Model Detection

FastAPI's Pydantic models are automatically detected and documented:

Field Types

from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime

class User(BaseModel):
    # Basic types
    id: int
    name: str
    email: str
    active: bool

    # Optional fields
    age: Optional[int] = None
    bio: Optional[str] = None

    # Lists and nested types
    tags: List[str] = []
    roles: List[str] = ["user"]

    # Dates
    created_at: datetime
    updated_at: Optional[datetime] = None

Field Validation

from pydantic import BaseModel, Field, validator

class CreateUserRequest(BaseModel):
    name: str = Field(
        ...,
        min_length=1,
        max_length=100,
        description="User's full name"
    )

    email: str = Field(
        ...,
        regex=r'^[\w\.-]+@[\w\.-]+\.\w+$',
        description="Valid email address"
    )

    age: int = Field(
        ...,
        ge=0,
        le=150,
        description="User's age"
    )

    password: str = Field(
        ...,
        min_length=8,
        description="Password (min 8 characters)"
    )

    @validator('password')
    def password_strength(cls, v):
        if not any(char.isdigit() for char in v):
            raise ValueError('Password must contain at least one digit')
        return v

Field Examples

from pydantic import BaseModel, Field

class User(BaseModel):
    id: int = Field(..., example=123)
    name: str = Field(..., example="John Doe")
    email: str = Field(..., example="john@example.com")
    age: int = Field(None, example=30, ge=0, le=150)

Nested Models

from pydantic import BaseModel
from typing import List

class Address(BaseModel):
    street: str
    city: str
    country: str
    zipcode: str

class User(BaseModel):
    id: int
    name: str
    email: str
    address: Address  # Nested model
    addresses: List[Address] = []  # List of nested models

Type Hints

Python type hints are used for auto-detection:

Basic Type Hints

from typing import Optional, List, Dict

@app.get("/users/{user_id}")
async def get_user(
    user_id: int,  # Path parameter
    include_posts: bool = False,  # Query parameter
) -> Dict[str, any]:  # Response type
    return {"id": user_id, "name": "John"}

Advanced Type Hints

from typing import Union, Literal

@app.get("/search")
async def search(
    query: str,
    type: Literal["users", "posts", "comments"],  # Enum
    limit: int = 10,
    offset: int = 0,
) -> Union[List[User], List[Post], List[Comment]]:
    # Multiple return types
    pass

FastAPI Dependencies

from fastapi import Depends, HTTPException, Header
from typing import Optional

async def get_current_user(
    authorization: str = Header(..., description="Bearer token")
) -> User:
    """
    Verify JWT token and return current user
    """
    # Token verification logic
    if not is_valid_token(authorization):
        raise HTTPException(status_code=401, detail="Invalid token")
    return get_user_from_token(authorization)

@app.get("/me", response_model=User)
async def get_my_profile(
    current_user: User = Depends(get_current_user)
):
    """
    Get current user's profile
    Requires authentication
    """
    return current_user

Response Models

Single Response

from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    return UserResponse(
        id=user_id,
        name="John",
        email="john@example.com"
    )

Multiple Status Codes

from fastapi import HTTPException
from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str

class ErrorResponse(BaseModel):
    detail: str

@app.get(
    "/users/{user_id}",
    response_model=UserResponse,
    responses={
        200: {"model": UserResponse, "description": "Success"},
        404: {"model": ErrorResponse, "description": "User not found"},
    }
)
async def get_user(user_id: int):
    user = get_user_from_db(user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

List Responses

from typing import List

@app.get("/users", response_model=List[UserResponse])
async def get_users(
    limit: int = 10,
    offset: int = 0
):
    users = get_users_from_db(limit, offset)
    return users

Paginated Responses

from pydantic import BaseModel
from typing import List, Generic, TypeVar

T = TypeVar('T')

class PaginatedResponse(BaseModel, Generic[T]):
    items: List[T]
    total: int
    page: int
    pages: int

@app.get("/users", response_model=PaginatedResponse[User])
async def get_users(page: int = 1, per_page: int = 10):
    users, total = get_users_paginated(page, per_page)
    return PaginatedResponse(
        items=users,
        total=total,
        page=page,
        pages=(total + per_page - 1) // per_page
    )

Documentation Docstrings

FastAPI

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    """
    Get user by ID

    Retrieve a specific user by their unique identifier.

    Args:
        user_id: The unique identifier of the user

    Returns:
        User object with all details

    Raises:
        404: User not found
    """
    return get_user_from_db(user_id)

Flask (YAML format)

@app.route('/users/<int:user_id>')
def get_user(user_id):
    """
    Get user by ID
    ---
    tags:
      - Users
    parameters:
      - name: user_id
        in: path
        type: integer
        required: true
    responses:
      200:
        description: User object
      404:
        description: User not found
    """
    return jsonify(get_user_from_db(user_id))

Environment Variables

# .env file
BYTEDOCS_TITLE="My API"
BYTEDOCS_VERSION="1.0.0"
BYTEDOCS_DESCRIPTION="My awesome API"
BYTEDOCS_DOCS_PATH=/docs
BYTEDOCS_AUTO_DETECT=true

# Multiple environments
BYTEDOCS_PRODUCTION_URL=https://api.example.com
BYTEDOCS_STAGING_URL=https://staging.example.com
BYTEDOCS_LOCAL_URL=http://localhost:8000

# Authentication
BYTEDOCS_AUTH_ENABLED=true
BYTEDOCS_AUTH_TYPE=session
BYTEDOCS_AUTH_USERNAME=admin
BYTEDOCS_AUTH_PASSWORD=secret

# AI
BYTEDOCS_AI_ENABLED=true
BYTEDOCS_AI_PROVIDER=openai
BYTEDOCS_AI_API_KEY=sk-...
BYTEDOCS_AI_MODEL=gpt-4o-mini

Using in Configuration

import os
from dotenv import load_dotenv

load_dotenv()

setup_bytedocs(app, {
    "title": os.getenv("BYTEDOCS_TITLE"),
    "version": os.getenv("BYTEDOCS_VERSION"),
    "description": os.getenv("BYTEDOCS_DESCRIPTION"),
    "auth": {
        "enabled": os.getenv("BYTEDOCS_AUTH_ENABLED") == "true",
        "type": os.getenv("BYTEDOCS_AUTH_TYPE"),
        "username": os.getenv("BYTEDOCS_AUTH_USERNAME"),
        "password": os.getenv("BYTEDOCS_AUTH_PASSWORD"),
    },
    "ai": {
        "enabled": os.getenv("BYTEDOCS_AI_ENABLED") == "true",
        "provider": os.getenv("BYTEDOCS_AI_PROVIDER"),
        "api_key": os.getenv("BYTEDOCS_AI_API_KEY"),
        "model": os.getenv("BYTEDOCS_AI_MODEL"),
    },
})

Production Deployment

Disable in Production

import os

if os.getenv("ENVIRONMENT") != "production":
    setup_bytedocs(app, {
        "title": "My API",
        "version": "1.0.0",
    })

Or Protect with Authentication

setup_bytedocs(app, {
    "title": "My API",
    "version": "1.0.0",
    "auth": {
        "enabled": os.getenv("ENVIRONMENT") == "production",
        "type": "session",
        "username": os.getenv("DOCS_USERNAME"),
        "password": os.getenv("DOCS_PASSWORD"),
    },
})

Best Practices

1. Use Pydantic Models (FastAPI)

# ✅ Good - Clear, auto-documented
class CreateUserRequest(BaseModel):
    name: str
    email: str

@app.post("/users")
async def create_user(user: CreateUserRequest):
    pass

# ❌ Avoid - Unclear, manual documentation needed
@app.post("/users")
async def create_user(name: str, email: str):
    pass

2. Add Response Models

# ✅ Good - Response schema documented
@app.get("/users/{id}", response_model=User)
async def get_user(id: int):
    return get_user_from_db(id)

# ❌ Avoid - Response schema unknown
@app.get("/users/{id}")
async def get_user(id: int):
    return get_user_from_db(id)

3. Use Type Hints

# ✅ Good - Types clear
async def get_user(user_id: int) -> User:
    pass

# ❌ Avoid - Types unclear
async def get_user(user_id):
    pass

4. Add Descriptions

# ✅ Good - Well documented
class User(BaseModel):
    id: int = Field(..., description="Unique user identifier")
    name: str = Field(..., description="Full name of the user")
    email: str = Field(..., description="Email address (must be unique)")

# ❌ Avoid - No descriptions
class User(BaseModel):
    id: int
    name: str
    email: str

Troubleshooting

Pydantic Models Not Detected

  1. Ensure response_model is set:
# ❌ Not detected
@app.get("/users/{id}")
async def get_user(id: int):
    return user

# ✅ Detected
@app.get("/users/{id}", response_model=User)
async def get_user(id: int):
    return user

FastAPI's Built-in Docs Conflict

# Disable FastAPI's built-in docs
app = FastAPI(
    docs_url=None,
    redoc_url=None,
    openapi_url=None
)

Routes Not Appearing

Ensure Bytedocs is setup after routes are defined:

# Define routes first
@app.get("/users")
async def get_users():
    pass

# Then setup Bytedocs
setup_bytedocs(app, config)

Visit Documentation

Start your server and visit:

http://localhost:8000/docs

What's Next?