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
- Clean Architecture - Separation of concerns with clear layers
- Extensibility - Extend base classes, don't modify them
- Type Safety - Full type hints throughout
- Async First - Built for async/await patterns
- 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)
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 rulesREADME.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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b5dd873b993a25fdc7a98d157fc48e30e5a658bce9e26faa1f25c7e91247ab8
|
|
| MD5 |
d1f2a884fff813772a8a54e0b5c0e155
|
|
| BLAKE2b-256 |
103f2bd96528f943cbd0954bb6954d4e7972d3775e4c73f7a087cc7faad19cd9
|
File details
Details for the file apex_saas_framework-0.1.10-py3-none-any.whl.
File metadata
- Download URL: apex_saas_framework-0.1.10-py3-none-any.whl
- Upload date:
- Size: 126.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f58fc4a8ca60690b2f7a83a24760770b03911c60afc494a6f137359d0692f5af
|
|
| MD5 |
63749d4a14facfaaad303d1b93b19d8a
|
|
| BLAKE2b-256 |
ec048102f94f8ccb265e5f2ba5750ecced61b034bb4e2c22d7f3532bbe721e48
|