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.5.tar.gz (109.3 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.5-py3-none-any.whl (132.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: python_axon-1.1.5.tar.gz
  • Upload date:
  • Size: 109.3 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.5.tar.gz
Algorithm Hash digest
SHA256 4f547efa2d6e6ed47517abcd7c12ce0fff62df12c44d8757ee6218b733ba95e6
MD5 b97e40dec836d0bf99c6f03993611dd4
BLAKE2b-256 47c8c0226a21811177ac31ee9a1a552bb587e78c8e267f8ca7b62c0645fc4098

See more details on using hashes here.

File details

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

File metadata

  • Download URL: python_axon-1.1.5-py3-none-any.whl
  • Upload date:
  • Size: 132.5 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.5-py3-none-any.whl
Algorithm Hash digest
SHA256 251057bfedda23a9a18f366ca98ed8f07cc2a321ccd54f7815122281356ec3ac
MD5 186e0d2858875eed3f57b8171c0172f6
BLAKE2b-256 3b5b19ce1810197d7ebd78ce1288eb1715a49710cae6a56bfc60f953f7c30196

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