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.0.9.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.0.9-py3-none-any.whl (135.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: python_axon-1.0.9.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.0.9.tar.gz
Algorithm Hash digest
SHA256 ef085199b048c6f0c4e7e0396be0968e039ee068c7094f5039447dd169b04cf0
MD5 c664e2060e001771ef3a78e00947299f
BLAKE2b-256 14580a3089a6c99f63e38f91398b8cb9204fcdc8ef04aa982f0376bfe70746e5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: python_axon-1.0.9-py3-none-any.whl
  • Upload date:
  • Size: 135.1 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.0.9-py3-none-any.whl
Algorithm Hash digest
SHA256 280428d1cabc6a46a1735658f71d50b64b32ef17e1ea4170ebfb8c3ef10e2e19
MD5 a69ea0fe9984b254e22706db219172dc
BLAKE2b-256 d3ef672fddba3bd618ae24ffe530b710ecf2136c37c171c326f0d41b72563218

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