Skip to main content

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 (opt-in driver-native async on Postgres), 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 (single-process or Redis-fanned-out)
DI Depends(...), FastAPI-style, nested, per-request cached
Validation optional pydantic (v2) — a typed param is validated from the body, 422 on failure
Testing TestClient — in-process ASGI client (HTTP + WebSocket), stdlib-only, no test server to run
Observability structured logging with secret masking, Prometheus metrics, OpenTelemetry tracing, /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

  • 2336 tests in the framework's own suite (routing matrices, ORM dialect parity, SQL-injection tests, migration rollback, DI edge cases), at 99.9%+ statement coverage — and not padding: every test asserts real behavior, status codes, or exceptions, not just "it imports".
  • 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 and Redis (tests/orm/test_postgres_pool.py, tests/orm/test_postgres_async_native.py), proving transaction isolation — sync and native-async — under concurrency before you'd trust it in production, not just on SQLite.
  • Cross-platform verified, not just declared — CI runs the full matrix (Python 3.11–3.13) against real Postgres + Redis on Linux; the one Windows-specific line in the whole codebase (an asyncio event-loop choice needed only for local psycopg development on Windows) is isolated to a single test helper, not the framework itself.
  • 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.
  • The public API is documented and covered by semver as of 1.0 — see API stability for exactly what's covered going forward.

A quick look at the ORM

from endocore.orm import Model, fields, configure, create_all, aatomic, 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 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.

Testing your app

from endocore import TestClient
from endocore.core.application import Application

client = TestClient(Application(app_dir="."))
resp = client.post("/v1/user", json={"name": "Ada"})
assert resp.status_code == 201

with TestClient(Application(app_dir=".")) as client:   # runs on_startup/on_shutdown for real
    with client.websocket_connect("/v1/chat") as ws:
        ws.send_text("hi")
        assert ws.receive_text() == "echo: hi"

No test server, no extra dependency — TestClient drives the same ASGI protocol uvicorn does, over in-memory queues.

→ Full testing guide: docs/guide/testing.md

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.

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, 1.1.0. The public API is covered by semver — see API stability.

Metadata

Release files for endocore 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for endocore 1.1.0
File Size Uploaded Provenance
endocore-1.1.0.tar.gz 174.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for endocore 1.1.0
File Interpreter ABI Platform
endocore-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 311.3 kB

Release files / endocore-1.1.0.tar.gz

Download URL endocore-1.1.0.tar.gz
Size 174.8 kB
Tags Source
SHA-256 checksum
How to use checksums
fae2a736245d16e6d0d972b98b475c856c8fa12401457580c240f0d98f377be5
BLAKE2b-256 checksum
How to use checksums
3ad97ccbcb1cb4fcce10ed3e1151ba9e222441531ec229b709ce3e7a95a52a8a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / endocore-1.1.0-py3-none-any.whl

Download URL endocore-1.1.0-py3-none-any.whl
Size 136.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
49168e5ad2c564b86caac04d5be1814f5ffbed3b8a760e13565a556c9b0d1aa4
BLAKE2b-256 checksum
How to use checksums
a3d35852749515118442c6ef26f132364b4da434ee2f4f69c03cd7511be5ee80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Security for endocore 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page