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

Uploaded Python 3

File details

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

File metadata

  • Download URL: python_axon-1.0.5.tar.gz
  • Upload date:
  • Size: 110.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.5.tar.gz
Algorithm Hash digest
SHA256 97a065828db3f874a554fc7edaadf87dccd0c82bdae1c4eb9044f29375befb57
MD5 e983ce083dcc8e2405cb5a957a383a72
BLAKE2b-256 f5b9d280f29dfcce6cd7b47aa61e3b47ba76dcb108f54bed53d2a650d7879440

See more details on using hashes here.

File details

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

File metadata

  • Download URL: python_axon-1.0.5-py3-none-any.whl
  • Upload date:
  • Size: 136.3 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.5-py3-none-any.whl
Algorithm Hash digest
SHA256 608edec2a0b6517c2f26050ad675bc4990f01655daeb0cb59152df68960b9565
MD5 1d9b8a95955d123fd45a4984be6402f6
BLAKE2b-256 f1a8f784e1b665d7c8e76cabdff7f9bc334310c1614305eb68fcd78dff7855f8

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