Skip to main content

File-based ASGI backend framework (folder tree = API) with an async-capable secure ORM, DI, WebSockets + pub/sub, cache, OpenAPI, migrations, and service integrations.

Project description

EndoCore

PyPI Python Tests License Discord

Your folder tree is your API. No routers. No decorators. No drift.

Api/v1/User/[id]/Get.py    ->  GET  /v1/user/42        (id="42")
Api/v1/User/Role/Post.py   ->  POST /v1/user/role
Api/v2/User/[id]/Get.py    ->  GET  /v2/user/42         (v1 keeps working, untouched)

Drop a file in the right folder. The endpoint exists — routed, versioned, and showing up in endo routes and /docs — with zero registration code. Delete the file, the endpoint is gone. Routes are discovered directly from the project structure, so there's no separate router table that can drift from what the code actually does.

# Api/v1/User/Role/Post.py  ->  POST /v1/user/role
from endocore import Request, Response

async def handler(request: Request) -> Response:
    data = await request.json()
    return Response.json({"created": data["name"]}, status=201)

That's a real, complete, working endpoint. No app = FastAPI(), no @app.post(...), no import to wire up anywhere. The file's path and name are the whole contract.

📖 Full documentation: https://endocore.readthedocs.io (EN / RU) — every guide below goes into far more depth than this README. 💬 Community: https://discord.gg/jwvGj2M9EX — questions, showcase, help.


The problem this solves

Every growing API codebase eventually drifts: the route table says one thing, the handler code says another, and "which version does this endpoint belong to?" becomes an archaeology exercise. Decorator-based routers make this worse as they scale — the routes live in your head, scattered across files that import each other in whatever order someone wrote them.

EndoCore eliminates that entire class of drift. The Api/ directory is the route table. endo routes doesn't introspect decorators or import your whole app to find them — it just reads the tree, because the tree already is the answer. A new API version (v2) is shutil.copytree with a filter, so v1 shares no router state with it and can't be touched by a v2 change — no if version == 2 branches rotting in a handler, no versioning that only works if everyone remembers the convention.

Application
  → Router (reads Api/ — no decorators, no import-time registration)
    → Middleware chain (logging, CORS, auth, ...)
      → Dependency injection (Depends(), providers, path params)
        → Your handler
          → ORM (optional) → SQLite / PostgreSQL

60 seconds to a running API

pip install endocore
endo new myapp && cd myapp
endo dev                       # http://127.0.0.1:8000/docs

endo new scaffolds a runnable app (Api/, Models/, Services/, Middleware/). endo dev boots it with the file watcher on — edit a handler, save, the route table reloads in-process (no restart). Open /docs for a live Swagger UI generated from your actual endpoints.

What you get, out of the box

One pip install, one process, no assembly required:

Routing & versioning folder tree = routes; vN folders coexist forever
ORM SQLite + PostgreSQL, sync and async, connection pooling, migrations with rollback
Security parameterized SQL only, quoted/validated identifiers, scrypt password hashing, signed sessions, CSRF, rate limiting
Real-time file-based WebSockets (Socket.py) + pub/sub rooms
DI Depends(...), FastAPI-style, nested, per-request cached
Validation optional pydantic — a typed param is validated from the body, 422 on failure
Ops structured logging with secret masking, /openapi.json + Swagger UI, cache (memory/Redis), background tasks
Integrations Redis, Celery, SMTP email — plug in via extensions.py
CLI endo create, endo dev, endo version create, endo makemigrations, endo routes, endo check, endo doctor

Exactly one required dependency: uvicorn. The resolver, loader, Request/Response, middleware chain, ORM and CLI are all standard library — nothing else is on the critical path of "does this framework start."

Proof, not promises

  • 1756 tests in the framework's own suite (routing matrices, ORM dialect parity, SQL-injection tests, migration rollback, DI edge cases).
  • Three full demo apps in demos/ that don't just run — they're race-tested under real concurrency: a kanban board with live WebSocket updates, a room-booking system where 8 simultaneous requests for the same slot produce exactly one winner, and a shop with idempotent purchases + payment-webhook handling that survives being retried mid-flight or hit by 6 concurrent duplicate requests without double-charging anyone.
  • A connection pool test suite that runs against real PostgreSQL (tests/orm/test_postgres_pool.py), proving transaction isolation under concurrency before you'd trust it in production — not just on SQLite.
  • An adversarial security audit, not just a linter run: response-splitting, a pickle-RCE cache path, cross-site WebSocket hijacking, and two ORM race conditions were found by reproducing the exploit, fixed, and re-verified — see the security guide and changelog. bandit + pip-audit now run in CI on every push, and the request parsers are property-fuzzed with hypothesis.

A quick look at the ORM

from endocore.orm import Model, fields, configure, create_all, Q, F

class User(Model):
    name   = fields.CharField(max_length=100)
    age    = fields.IntegerField(default=0)
    active = fields.BooleanField(default=True)

configure(backend="sqlite", database="app.db")   # or backend="postgres", pool_size=10, ...
create_all(User)

User.objects.create(name="Ada", age=36)
User.objects.filter(age__gte=18).order_by("-age")             # lazy QuerySet
User.objects.filter(Q(age__lt=18) | Q(name__icontains="a"))   # Q objects
User.objects.filter(age__gte=18).update(active=True)          # bulk update
User.objects.filter(pk=1).update(age=F("age") + 1)            # atomic F() expression

# non-blocking, for ASGI handlers:
user = await User.objects.aget(pk=1)
async with endocore.orm.aatomic():
    await user.asave()

Every value is bound through the driver (never string-formatted into SQL), every identifier is validated and quoted, only a fixed whitelist of lookups produces SQL, and LIMIT/OFFSET are coerced to int. This isn't a layer you opt into — it's the only way the ORM knows how to build a query.

→ Full ORM reference: docs/orm/ — fields, relations, aggregates, transactions, migrations, encrypted files.

How it compares

EndoCore FastAPI Django
Routing file path = route decorators decorators (urls.py)
Versioning vN folders, built in manual manual (separate apps)
ORM built in (sync + async) none (bring your own) built in (sync)
Migrations built in, with rollback Alembic (separate) built in
Core dependencies 1 (uvicorn) Starlette + pydantic none (own stack)
Codebase size small enough to read in an afternoon large very large

→ Full comparison with trade-offs: docs/comparison.md

Coming from FastAPI?

The mental model carries over directly — same ASGI deployment, a similar Request/Response shape, the same Depends(...) pattern. What changes is where a route lives: instead of a decorator wherever you happened to put it, POST /v1/user/role is the file Api/v1/User/Role/Post.py. You can migrate incrementally, endpoint by endpoint, since nothing about one route's file has anything to do with another's.

Everything else is one link away

The docs cover every corner in depth — routing rules and edge cases, dependency injection resolution order, middleware ordering, every ORM field and lookup, transactions and connection pooling, migrations, WebSockets and pub/sub, caching, service integrations, security hardening, deployment, and a full step-by-step tutorial:

📖 https://endocore.readthedocs.io (English / Russian, kept in sync)

py -3 -m pip install -e ".[dev]"      # clone + install for local development
pytest -q                              # the framework's own test suite
cd example && endo dev                  # run the bundled example app

Run the framework's own tests with pytest; run an app's tests with endo test.


EndoCore is built around one idea: your directory tree should already describe your API. No duplicated routing tables, no hidden registration, no drift between what the code does and what the docs say it does — just files, folders, and behavior you can predict by looking at where things live.

License

MIT. See LICENSE. Contributions: docs/contributing.md. Changelog: CHANGELOG.md.

Personal / sporting-interest project, Beta (0.9.0b1). Client-usable today; stabilizing toward 1.0.

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

endocore-0.9.0b1.tar.gz (128.2 kB view details)

Uploaded Source

Built Distribution

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

endocore-0.9.0b1-py3-none-any.whl (121.3 kB view details)

Uploaded Python 3

File details

Details for the file endocore-0.9.0b1.tar.gz.

File metadata

  • Download URL: endocore-0.9.0b1.tar.gz
  • Upload date:
  • Size: 128.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for endocore-0.9.0b1.tar.gz
Algorithm Hash digest
SHA256 ea0bb070eccbb253cec031147fb602eaa605e0f500b58d40d5a5a0bbe59c7664
MD5 d74aa671ac021e92c3db7b9abcf1a90c
BLAKE2b-256 d0796502de70323c1bce3c5aa4be67847e60757162ee10b84e1971b81e2f6654

See more details on using hashes here.

File details

Details for the file endocore-0.9.0b1-py3-none-any.whl.

File metadata

  • Download URL: endocore-0.9.0b1-py3-none-any.whl
  • Upload date:
  • Size: 121.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for endocore-0.9.0b1-py3-none-any.whl
Algorithm Hash digest
SHA256 23fa5df187af25573e63a77734a8bdafbe7816a0fdda67427e6cf1c55a301a6d
MD5 1156f8039cd535ca8274e3d10aeed95e
BLAKE2b-256 4e45d06a50a0d04c4c8f24a53ccafe6092ccd51fa2ef20d1b4188393628c34f6

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