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.0.tar.gz (99.9 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.0-py3-none-any.whl (121.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: python_axon-1.1.0.tar.gz
  • Upload date:
  • Size: 99.9 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.0.tar.gz
Algorithm Hash digest
SHA256 3abf6ca5fe58a9efb268885be276c531bedd1c23140ccfce19e4ac52e6ec4f73
MD5 9f11d74e854ff8df88bbec2c62bb4083
BLAKE2b-256 f8280825f3d863f1be6787a76e988e1a2dd775743e7bb790fb577c6189b33319

See more details on using hashes here.

File details

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

File metadata

  • Download URL: python_axon-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 121.7 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 108136ea37922425c55aac397bb0239207f7ff79bf00580166d4feedad400864
MD5 d95176eeade599ae900ed0dc5b95f13b
BLAKE2b-256 54f66f7b649d58bc1783d5d03e3a7109887081e8718b897d03dfb8c1a9742447

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