Skip to main content

A modular Python backend framework built on FastAPI

Project description

Axon Framework

A modular Python backend framework built on FastAPI. Inspired by NestJS and Laravel — modules, DI container, hook bus, scheduler, and a full CLI — without the boilerplate.

Install

pip install python-axon

Create a project

axon create_project myapp
cd myapp
pip install -r requirements.txt
./manage migrate
./manage runserver

Your API is live at http://localhost:8000. Docs at http://localhost:8000/docs.


Import surface

Everything a module builder needs comes from three places:

# Core classes and all helpers
from axon import Module, Service, Model, Router, Manifest, Context, Log
from axon import bind, make, override, extend, env, config, abort

# Database field types
from axon.fields import StringField, BoolField, IntField, ForeignKey, ChoiceField

# Auth guards for routes
from axon.auth import require_auth, require_role, optional_auth

# HTTP error responses
from axon.responses import not_found, bad_request, forbidden, unprocessable

Old deep imports (from axon.core.*) continue to work — nothing breaks.


Scaffold a module

./manage make:module orders

Generates app/modules/orders/ with models, services, routes, schemas, hooks, and a manifest — all wired and ready.


Module anatomy

# app/modules/orders/__init__.py
from axon import Module, bind, on, env, Log

class Module(Module):

    @property
    def name(self) -> str:
        return "orders"

    def register(self) -> None:
        bind(OrderService)          # bare bind() — same as self.bind()

    async def boot(self) -> None:
        on("platform.booted", self._ready)

    async def _ready(self, payload: dict) -> None:
        Log.i("orders", "module ready")

Model

# app/modules/orders/models.py
from axon import Model as AxonModel
from axon.fields import StringField, DecimalField, ChoiceField, ForeignKey

class Order(AxonModel):
    status  = ChoiceField(["pending", "paid", "shipped"], default="pending")
    total   = DecimalField(precision=10, scale=2)
    user_id = ForeignKey("auth_users.id")

Service

# app/modules/orders/services.py
from axon import Service as AxonService, Log

class OrderService(AxonService):

    def create(self, data: dict) -> dict:
        with self.db().session() as s:
            order = Order(**data)
            s.add(order)
            s.flush()
            Log.i("OrderService", "created id=%d", order.id)
            return order.to_dict()

Routes

# app/modules/orders/routes.py
from axon import Router
from axon.auth import require_auth
from axon.responses import not_found
from .services import OrderService
from .schemas import OrderIn

router = Router(prefix="/orders", tags=["orders"])

@router.get("/")
async def index(ctx, svc: OrderService):
    return svc.list()

protected = router.group("", middleware=[require_auth])

@protected.post("/", status=201)
async def store(body: OrderIn, ctx, svc: OrderService):
    return svc.create(body.model_dump())

Manifest

# app/modules/orders/manifest.py
from axon import Manifest

manifest = Manifest(
    name        = "orders",
    version     = "1.0.0",
    description = "Order management",
    requires    = ["auth"],
    provides    = ["order_svc"],   # make("order_svc") resolves OrderService
    enabled     = True,
    routes      = "routes",
)

Helpers

from axon import (
    # Environment
    env, env_bool, env_int, env_list,

    # Config
    config, config_set,

    # Container (bare — no self. needed)
    bind, override, extend, make, instance, has_binding,

    # Events and jobs
    on, emit, filter_event, dispatch, schedule,

    # HTTP
    abort, url, route,

    # Strings
    str_slug, str_snake, str_camel, str_pascal, str_uuid, str_random,
    str_headline, str_truncate, str_pad, str_between, str_replace,

    # Arrays / collections
    arr_get, arr_set, arr_only, arr_except, arr_pluck, arr_group_by,
    arr_sort, arr_filter, arr_map, arr_first, arr_last, arr_chunk,
    arr_sum, arr_avg, arr_min, arr_max, arr_key_by, arr_partition,
    arr_merge, arr_deep_merge,

    # Values
    blank, filled, optional, tap, once, retry,

    # Security
    bcrypt, bcrypt_check, hash_make, hash_check,
    md5, sha256, hmac_sign, hmac_verify,

    # Numeric
    num_format, num_currency, num_percent, num_ordinal, num_clamp,

    # DateTime
    now, today, timestamp, now_iso, human_time, diff_in_seconds,

    # Files
    file_exists, file_get, file_put, file_extension, file_basename,

    # Debug
    dd, dump,
)

Database configuration

config.yaml supports both a raw connection string and a structured block:

platform:
  # Option A — raw connection string (takes priority if set)
  database_url: "postgresql://user:pass@localhost/mydb"

  # Option B — structured block
  database:
    engine:   postgresql     # sqlite | postgresql | mysql | mariadb | mssql
    name:     mydb
    host:     localhost
    port:     5432
    user:     myuser
    password: secret
    echo:     false
    pool_size: 5

Override any value with env vars:

PLATFORM__DATABASE__ENGINE=postgresql
PLATFORM__DATABASE__HOST=db.prod.internal
PLATFORM__DATABASE__PASSWORD=s3cret

CLI reference

# Run from anywhere
axon create_project <name>         Bootstrap a new project

# Run inside a project (./manage <command>)
make:module <name>                 Scaffold a new module
migrate                            Run pending migrations
makemigrations [name]              Generate a migration
migrate:rollback                   Roll back the last migration
migrate:fresh --seed               Wipe DB and re-run from scratch
runserver [host] [port]            Start dev server (default: 0.0.0.0:8000)
createsuperuser                    Create an admin user
status                             Show platform status
shell                              Python REPL with full platform context
dbshell                            Native DB shell (psql / sqlite3)

Build and publish

pip install build twine
python -m build
twine upload dist/*

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

python_axon-1.1.4.tar.gz (109.0 kB view details)

Uploaded Source

Built Distribution

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

python_axon-1.1.4-py3-none-any.whl (132.2 kB view details)

Uploaded Python 3

File details

Details for the file python_axon-1.1.4.tar.gz.

File metadata

  • Download URL: python_axon-1.1.4.tar.gz
  • Upload date:
  • Size: 109.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for python_axon-1.1.4.tar.gz
Algorithm Hash digest
SHA256 63bcb966ca75383ffa0ba4de1a1d647bdabb2b1cfedb09eaabcd9bc4d644df67
MD5 fd9617c2d5156eec29f7946600d0d03d
BLAKE2b-256 58044411a5da7cadcc415206d093d5ff53abcc0b89ebea490f29a9ed838c3640

See more details on using hashes here.

File details

Details for the file python_axon-1.1.4-py3-none-any.whl.

File metadata

  • Download URL: python_axon-1.1.4-py3-none-any.whl
  • Upload date:
  • Size: 132.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for python_axon-1.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 79d61c1965e81e78893dda25e2124e729958be3499cafa4352546bc5316ef7af
MD5 30d15020690c7b5fe1cdd8cd9895b3b6
BLAKE2b-256 4f30531fad41c593b63fb144d0b602b92a11ba9fcef3e4aac55bf124525c7fb0

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