Skip to main content

A pure Python SDK for multi-tenant SaaS (users, auth, RBAC, payments, email, files), database-agnostic and framework-agnostic.

Project description

Apex SaaS Framework

A pure Python library - No APIs provided. You write your own APIs using the features you need.

  • No API endpoints - You write your own APIs (FastAPI, Flask, Django, etc.)
  • Use only what you need - Pick the features you want
  • Your database choice - SQLite, PostgreSQL, MySQL, or any SQLAlchemy-supported database
  • Minimal code - Just database URL and you're ready

⚡ Super Quick Start

# my_app.py - YOU CREATE THIS FILE
from apex import Client
from apex.core.base import Base
from sqlalchemy import Column, Integer, String

# Define your models (your choice - primary keys, foreign keys, fields!)
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)  # Your primary key choice
    email = Column(String(255), unique=True)
    password_hash = Column(String(255))

# Initialize - Just database URL (your choice!)
client = Client(
    database_url="sqlite+aiosqlite:///./mydb.db",  # SQLite (easiest)
    # or "postgresql+asyncpg://..." for PostgreSQL
    # or "mysql+aiomysql://..." for MySQL
    user_model=User
)

# Use it in your code (you write your own APIs!)
async with client:
    await client.init_database()
    user = await client.users.create(email="user@example.com", password="pass123")
    # Use in your FastAPI/Flask/Django endpoints

That's it! You write your own APIs using the features you need! 🎉

🎯 Key Features

  • Pure Python Library - No APIs provided, you write your own
  • Use Only What You Need - Pick features: users, auth, payments, email, etc.
  • Your Database Choice - SQLite, PostgreSQL, MySQL, or any SQLAlchemy-supported database
  • Flexible Models - Use all default fields, disable unwanted ones, or add custom fields
  • Minimal Code - Just database URL and you're ready
  • Framework Agnostic - Works with FastAPI, Flask, Django, or standalone

📝 Complete Model Control

Option 1: Define Everything Yourself (Full Control)

from apex import Client
from apex.core.base import Base
from sqlalchemy import Column, Integer, String, ForeignKey

# You define primary key, foreign keys, fields - everything!
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)  # Your primary key choice
    email = Column(String(255), unique=True)
    password_hash = Column(String(255))
    company_id = Column(Integer, ForeignKey("companies.id"))  # Your foreign key

class Company(Base):
    __tablename__ = "companies"
    id = Column(Integer, primary_key=True)
    name = Column(String(255))

Option 2: Use Flexible Helpers (Optional)

from apex import FlexibleUser, IntegerPKMixin

class User(FlexibleUser, IntegerPKMixin):  # Integer primary key
    __tablename__ = "users"
    # Add your own fields, foreign keys, etc.

Option 3: Mix and Match

from apex.core.base import Base, TimestampMixin
from sqlalchemy import Column, String

class User(Base, TimestampMixin):  # Use timestamps mixin
    __tablename__ = "users"
    user_id = Column(String(50), primary_key=True)  # Your custom primary key
    email = Column(String(255))

Key Points:

  • You choose primary key type (Integer, String, UUID, or any type)
  • You define foreign keys (any name, any reference)
  • You add any fields (any fields, any names)
  • Package adapts to your structure automatically

See CLERK_STYLE_GUIDE.md and DYNAMIC_MODELS_GUIDE.md for complete documentation.

🚀 Use It Anywhere (no bundled APIs)

The package provides functions only. You can call them from any Python environment—scripts, CLI tools, background workers, or any web framework you choose.

Available Features (use only what you need):

  • ✅ User management
  • ✅ Authentication (JWT tokens)
  • ✅ Organization management
  • ✅ Role-based permissions (RBAC)
  • ✅ PayPal payment integration
  • ✅ Email sending
  • ✅ File storage
  • ✅ Settings management
  • ✅ Feature flags (modules)

📦 Installation

From PyPI (Production - Recommended)

Simple installation:

pip install apex-saas-framework

That's it! No special flags needed! ✅

From Test PyPI (For Testing)

If you want to test pre-release versions:

pip install --index-url https://pypi.org/simple/ --extra-index-url https://test.pypi.org/simple/ apex-saas-framework

🚀 Features

  • 🎯 Simple API - Minimal imports, maximum functionality
  • 🏢 Multi-tenant Architecture - Organizations with row-level isolation
  • 👥 User Management - Complete user system with JWT authentication
  • 🔐 Authentication - Signup, login, forgot/reset password, change password
  • 🛡️ RBAC - Role-based access control with permissions
  • 📦 Extensible Models - Extend base models using ORM inheritance
  • 🗄️ PostgreSQL - SQLAlchemy 2.0 with async support
  • 💳 PayPal Integration - Subscription management and webhooks
  • ⚙️ JSONB Settings - Flexible user and organization settings
  • 🎨 Feature Flags - Module system for enabling/disabling features
  • 📧 Email Integration - SendGrid adapter with templates
  • 🛠️ CLI Tools - Command-line utilities

💡 Simple Examples

Minimal Usage

from apex import Client
from apex.core.base import Base
from sqlalchemy import Column, Integer, String

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True)
    password_hash = Column(String(255))

client = Client(database_url="sqlite+aiosqlite:///./mydb.db", user_model=User)

async with client:
    await client.init_database()
    user = await client.users.create(email="user@example.com", password="pass123")

🎯 Quick Start Guide

1. Create Your Models

Start by re-exporting the Apex defaults so each table is only registered once:

# app/models.py
from apex.app.models.default import (
    Organization,
    OrganizationLocation,
    Permission,
    Role,
    User,
)

__all__ = ["Organization", "OrganizationLocation", "Permission", "Role", "User"]

Need custom columns? Create new subclasses (e.g. class CustomUser(User): ...) in a separate module and update the dependency overrides (get_user_service, get_organization_model, etc.) to point at them. Avoid redefining the stock tables in place—SQLAlchemy will raise “Table … already defined” otherwise.

2. Configure Settings

Copy .env.example to .env and update with your values:

cp .env.example .env

Minimum required configuration:

# Database (Required)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

# Security (Required)
SECRET_KEY=your-secret-key-here-minimum-32-characters

Generate a secure secret key:

python -c "import secrets; print(secrets.token_urlsafe(32))"

See Configuration Reference below for all available options.

3. Set Up Database

# app/database.py
from apex.infrastructure.database import Base, engine
from app.models import User, Organization, Role, Permission

# Create tables
async def init_db():
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

4. Create Your FastAPI App

# app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from apex.api.v1.auth.router import router as auth_router
from apex.core.authentication.dependencies import get_auth_service
from apex.core.config import get_settings
from app.dependencies import get_auth_service as project_get_auth_service

settings = get_settings()

app = FastAPI(
    title=settings.APP_NAME,
    version=settings.APP_VERSION,
    debug=settings.DEBUG,
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=settings.CORS_ALLOW_CREDENTIALS,
    allow_methods=settings.CORS_ALLOW_METHODS,
    allow_headers=settings.CORS_ALLOW_HEADERS,
)

app.dependency_overrides[get_auth_service] = project_get_auth_service
app.include_router(auth_router, prefix=settings.API_V1_PREFIX)

@app.get("/")
async def root():
    return {"message": "Apex"}

5. Override Auth Service

# app/services.py
from apex.domain.services.auth import AuthService
from app.models import User
from sqlalchemy.ext.asyncio import AsyncSession

def get_auth_service(db: AsyncSession) -> AuthService:
    return AuthService(session=db, user_model=User)

6. Run Migrations

Set up Alembic:

alembic init migrations

Copy apex/migrations/env.py to your project and modify it to import your models:

# migrations/env.py
from apex.infrastructure.database.base import Base
from app.models import User, Organization, Role, Permission

target_metadata = Base.metadata

Create and run migrations:

alembic revision --autogenerate -m "Initial migration"
alembic upgrade head

Architecture

Project Structure

apex/
├── core/                    # Core functionality
│   ├── config/              # Settings (Pydantic BaseSettings)
│   ├── security/            # Password hashing, JWT utilities
│   ├── authentication/      # Auth dependencies (get_current_user, etc.)
│   ├── permissions/         # RBAC dependencies (require_role, require_permission)
│   └── utils/               # Utility functions
├── domain/                  # Business logic layer
│   ├── models/              # Base domain models (abstract)
│   │   ├── user.py          # BaseUser model
│   │   ├── organization.py  # BaseOrganization, BaseOrganizationLocation
│   │   ├── role.py          # BaseRole model
│   │   └── permission.py    # BasePermission model
│   ├── repositories/        # Repository pattern (BaseRepository)
│   └── services/            # Business services (AuthService, UserService)
├── infrastructure/          # External adapters
│   ├── database/            # Database setup (SQLAlchemy async, session mgmt)
│   ├── email/               # Email adapters (SMTP, abstract interface)
│   ├── storage/             # File storage adapters (Local, S3-ready)
│   └── payments/            # Payment scaffolding (Stripe-ready)
├── api/                     # API layer
│   └── v1/                  # API version 1
│       ├── auth/            # Authentication routes
│       │   └── router.py    # Login, logout, refresh, me endpoints
│       └── schemas/         # Pydantic request/response schemas
│           ├── auth.py      # Auth schemas
│           └── user.py      # User schemas
├── migrations/              # Alembic templates
│   ├── env.py               # Alembic environment (copy to user project)
│   └── script.py.mako       # Migration script template
└── cli/                     # CLI utilities (Typer)
    └── main.py              # CLI commands (version, check, init-db, etc.)

Design Principles

  1. Clean Architecture - Separation of concerns with clear layers
  2. Extensibility - Extend base classes, don't modify them
  3. Type Safety - Full type hints throughout
  4. Async First - Built for async/await patterns
  5. Framework Agnostic Core - Business logic independent of FastAPI

Usage Examples

Authentication

from apex.domain.services.auth import AuthService
from app.models import User

# Login
auth_service = AuthService(session=db, user_model=User)
user = await auth_service.authenticate_user("user@example.com", "password")
tokens = await auth_service.create_tokens(user)

User Management

from apex.domain.services.user import UserService

user_service = UserService(session=db, user_model=User)

# Create user
user = await user_service.create_user(
    email="user@example.com",
    password="securepassword",
    full_name="John Doe",
)

# Update user
await user_service.update_user(user, full_name="Jane Doe")

# Change password
await user_service.change_password(user, "oldpass", "newpass")

Permissions

from apex.core.permissions import require_permission, require_role

# In your router
@router.post("/users")
async def create_user(
    current_user: dict = Depends(require_permission("users", "create"))
):
    # User has "users:create" permission
    pass

@router.delete("/users/{id}")
async def delete_user(
    id: str,
    current_user: dict = Depends(require_role("admin"))
):
    # User has "admin" role
    pass

File Storage

from apex.infrastructure.storage import LocalStorageAdapter

storage = LocalStorageAdapter()

# Upload file
file_path = await storage.upload_file(
    file=uploaded_file,
    filename="document.pdf",
    folder="documents"
)

# Get file URL
url = await storage.get_file_url(file_path)

Email

from apex.infrastructure.email import SMTPEmailAdapter

email = SMTPEmailAdapter()

await email.send_email(
    to="user@example.com",
    subject="Welcome!",
    body="Welcome to our platform",
    html="<h1>Welcome!</h1>"
)

Configuration

All configuration is done via environment variables or .env file. Copy .env.example to .env and customize.

Required Settings

Variable Description Example
DATABASE_URL PostgreSQL connection string postgresql://user:pass@localhost:5432/db
SECRET_KEY JWT secret key (min 32 chars) Generate with secrets.token_urlsafe(32)

Application Settings

Variable Description Default
APP_NAME Application name "Apex Backend"
DEBUG Debug mode False
API_V1_PREFIX API version 1 prefix "/api/v1"

Database Settings

Variable Description Default
DB_ECHO Log SQL queries False
DB_POOL_SIZE Connection pool size 5
DB_MAX_OVERFLOW Max overflow connections 10

JWT/Auth Settings

Variable Description Default
ALGORITHM JWT algorithm "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES Access token lifetime 30
REFRESH_TOKEN_EXPIRE_DAYS Refresh token lifetime 7

CORS Settings

Variable Description Default
CORS_ORIGINS Allowed origins (comma-separated) "http://localhost:3000"
CORS_ALLOW_CREDENTIALS Allow credentials True
CORS_ALLOW_METHODS Allowed HTTP methods "GET,POST,PUT,DELETE,PATCH,OPTIONS"
CORS_ALLOW_HEADERS Allowed headers "*"

Email Settings (Optional)

Variable Description Example
SMTP_HOST SMTP server smtp.gmail.com
SMTP_PORT SMTP port 587
SMTP_USER SMTP username your-email@gmail.com
SMTP_PASSWORD SMTP password Your app password
SMTP_FROM_EMAIL From email address noreply@yourdomain.com
SMTP_USE_TLS Use TLS True

Storage Settings

Variable Description Default
STORAGE_TYPE Storage type (local or s3) "local"
STORAGE_LOCAL_PATH Local storage path "./uploads"
AWS_ACCESS_KEY_ID AWS access key (if using S3) -
AWS_SECRET_ACCESS_KEY AWS secret key (if using S3) -
AWS_REGION AWS region (if using S3) "us-east-1"
AWS_S3_BUCKET S3 bucket name (if using S3) -

Payment Settings (Optional)

Variable Description Example
STRIPE_SECRET_KEY Stripe secret key sk_test_...
STRIPE_PUBLISHABLE_KEY Stripe publishable key pk_test_...
STRIPE_WEBHOOK_SECRET Stripe webhook secret whsec_...

Multi-tenancy

Variable Description Default
MULTI_TENANT_MODE Enable multi-tenancy True

CLI Commands

The apex command-line tool provides utilities for managing your application:

# Show package version
apex version

# Check configuration and database connection
apex check

# Initialize database (base implementation - extend in your app)
apex init-db

# Create superuser (base implementation - extend in your app)
apex create-superuser --email admin@example.com --password

Note: init-db and create-superuser are base implementations. You should extend these commands in your application to work with your specific User model.

Extending the Framework

Custom User Model

class User(BaseUser):
    __tablename__ = "users"
    
    # Add custom fields
    avatar_url: Mapped[str | None] = None
    bio: Mapped[str | None] = None

Custom Service

from apex.domain.services.user import UserService

class CustomUserService(UserService):
    async def get_users_by_organization(self, org_id: str):
        # Custom logic
        pass

Custom Repository

from apex.domain.repositories.base import BaseRepository

class UserRepository(BaseRepository[User]):
    async def get_by_email(self, email: str) -> User | None:
        # Custom query
        pass

Testing

from fastapi.testclient import TestClient
from apex.infrastructure.database import get_db

# Override database dependency
app.dependency_overrides[get_db] = get_test_db

client = TestClient(app)
response = client.post("/api/v1/auth/login", json={
    "email": "test@example.com",
    "password": "password"
})

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE file for details.

Project Files

  • pyproject.toml - Package configuration and dependencies
  • .env.example - Template for environment variables (copy to .env)
  • .gitignore - Git ignore patterns
  • .cursor/rules/cursorrules.mdc - Cursor AI editing rules
  • README.md - This file

Support

Roadmap

  • S3 storage adapter implementation
  • Stripe payment integration
  • Audit logging system
  • Module/feature flag system
  • WebSocket support
  • GraphQL support
  • More comprehensive tests
  • Docker deployment examples

Built with ❤️ using FastAPI, SQLAlchemy, and modern Python practices.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apex_saas_framework-0.1.10.tar.gz (102.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apex_saas_framework-0.1.10-py3-none-any.whl (126.4 kB view details)

Uploaded Python 3

File details

Details for the file apex_saas_framework-0.1.10.tar.gz.

File metadata

  • Download URL: apex_saas_framework-0.1.10.tar.gz
  • Upload date:
  • Size: 102.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for apex_saas_framework-0.1.10.tar.gz
Algorithm Hash digest
SHA256 2b5dd873b993a25fdc7a98d157fc48e30e5a658bce9e26faa1f25c7e91247ab8
MD5 d1f2a884fff813772a8a54e0b5c0e155
BLAKE2b-256 103f2bd96528f943cbd0954bb6954d4e7972d3775e4c73f7a087cc7faad19cd9

See more details on using hashes here.

File details

Details for the file apex_saas_framework-0.1.10-py3-none-any.whl.

File metadata

File hashes

Hashes for apex_saas_framework-0.1.10-py3-none-any.whl
Algorithm Hash digest
SHA256 f58fc4a8ca60690b2f7a83a24760770b03911c60afc494a6f137359d0692f5af
MD5 63749d4a14facfaaad303d1b93b19d8a
BLAKE2b-256 ec048102f94f8ccb265e5f2ba5750ecced61b034bb4e2c22d7f3532bbe721e48

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page