Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

half-orm-litestar

A halfORM extension that generates a Litestar REST API from your halfORM relation classes by decorating their methods.

Installation

pip install half-orm-litestar

Requires half-orm-dev to be installed (for project introspection):

pip install half-orm-dev

Quick start

1. Decorate your halfORM methods

In any halfORM relation class, import tools and annotate the methods you want to expose as API routes:

# myproject/actor/user.py

from half_orm_litestar import tools

class User(MODEL.get_relation_class('actor.user')):

    @tools.api_get('/user/{id: uuid}', guards=['public'])
    async def get_user(self, request: "Request"):
        actor_id = request.path_params['id']
        return self.load_user(actor_id)

    @tools.api_get('/user/{user_id: uuid}/group_accesses', guards=['has_user_access'])
    async def get_user_group_accesses(self, request: "Request"):
        actor_id = request.path_params['user_id']
        return [dict(row) for row in self.rfk_group_accesses(actor_id=actor_id)]

    @tools.api_post('/users', guards=['connected'])
    async def create_user(self, request: "Request"):
        data = await request.json()
        ...

The decorators accept the same arguments as the corresponding Litestar decorators (@get, @post, @put, @patch, @delete).

Available decorators:

Decorator HTTP method
tools.api_get GET
tools.api_post POST
tools.api_put PUT
tools.api_patch PATCH
tools.api_delete DELETE

2. Generate the API

From the root of your half-orm-dev project:

half_orm litestar generate

This command:

  1. Scans all halfORM relation classes for @api_* decorated methods.
  2. Creates missing scaffolding files in api/ (only on first run — existing files are never overwritten).
  3. Writes api/app.py, the ready-to-run Litestar application.

3. Run the API

uvicorn api.app:application --reload

Project structure after generate

myproject/
├── api/
│   ├── __init__.py
│   ├── guards.py                        ← authentication guards (edit this)
│   ├── app.py                          ← generated, do not edit by hand
│   └── custom/
│       ├── __init__.py
│       ├── routes.py                    ← hand-written extra routes
│       └── middlewares/
│           ├── __init__.py              ← extra middlewares list
│           └── authorization.py        ← authentication middleware (edit this)

Files in api/ (except app.py) are created once and never overwritten. Regenerating only rewrites app.py.


Customisation

Guards (api/guards.py)

Guards are async callables that receive the ASGI connection and raise an exception to deny access. Two minimal guards are provided out of the box: public (allow all) and connected (require authenticated user).

Add your own project-specific guards in api/guards.py:

# api/guards.py
from litestar.connection import ASGIConnection
from litestar.handlers.base import BaseRouteHandler
from litestar.exceptions import HTTPException

async def has_user_access(
    connection: ASGIConnection, handler: BaseRouteHandler = None
) -> None:
    """Allow only the user identified by the ``user_id`` path parameter."""
    if not connection.user:
        raise HTTPException(status_code=401)
    if connection.user != str(connection.path_params.get('user_id')):
        raise HTTPException(status_code=403)

Then reference the guard by name in your decorator:

@tools.api_get('/user/{user_id: uuid}/profile', guards=['has_user_access'])
async def get_profile(self, request: "Request"):
    ...

Authorization middleware (api/custom/middlewares/authorization.py)

Implement the Authorization class to decode tokens / sessions and populate connection.user. When the file exists, Authorization is automatically placed first in the middleware stack.

# api/custom/middlewares/authorization.py
import jwt
from litestar.middleware import AbstractAuthenticationMiddleware, AuthenticationResult
from litestar.connection import ASGIConnection

SECRET = "change-me"

class Authorization(AbstractAuthenticationMiddleware):
    async def authenticate_request(self, connection: ASGIConnection) -> AuthenticationResult:
        token = (
            connection.headers.get("Authorization", "")
            .removeprefix("Bearer ")
            .strip()
        )
        try:
            payload = jwt.decode(token, SECRET, algorithms=["HS256"])
            return AuthenticationResult(user=payload["sub"], auth=token)
        except Exception:
            return AuthenticationResult(user=None, auth=None)

Extra routes (api/custom/routes.py)

Hand-written Litestar route handlers can be added here:

# api/custom/routes.py
from litestar import get

@get('/health')
async def health_check() -> dict:
    return {'status': 'ok'}

routes = [health_check]

Extra middlewares (api/custom/middlewares/__init__.py)

# api/custom/middlewares/__init__.py
from .my_middleware import MyMiddleware

middlewares = [MyMiddleware]

Path prefix

The generated routes are automatically prefixed with the project's database name. For a project named mydb, '/user/{id: uuid}' becomes '/mydb/user/{id: uuid}'.


Advanced: using without half-orm-dev

GenApi can be used programmatically without a Repo object by supplying the relation classes directly:

from half_orm_litestar.generate import GenApi

GenApi(
    relation_classes=my_classes,   # iterable of (RelationClass, type) pairs
    module_name='mydb',
    base_dir='/path/to/project',
)

Metadata

Release files for half-orm-litestar 1.0.0a5

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

Source distribution (sdist)

Source distribution for half-orm-litestar 1.0.0a5
File Size Uploaded Provenance
half_orm_litestar-1.0.0a5.tar.gz 29.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for half-orm-litestar 1.0.0a5
File Interpreter ABI Platform
half_orm_litestar-1.0.0a5-py3-none-any.whl Python 3 none any Details

Total release size: 55.8 kB

Release files / half_orm_litestar-1.0.0a5.tar.gz

Download URL half_orm_litestar-1.0.0a5.tar.gz
Size 29.0 kB
Tags Source
SHA-256 checksum
How to use checksums
72eda25083212e6b3d008ee2965249400dfa148ac1defe05a47d17c9517549f1
BLAKE2b-256 checksum
How to use checksums
8b34fde7cf06a12bbc841b31f9262b13dbb49d11ea7c266903bf04b257bd2a41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release files / half_orm_litestar-1.0.0a5-py3-none-any.whl

Download URL half_orm_litestar-1.0.0a5-py3-none-any.whl
Size 26.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
088357d0543dfa713a1608c25836f1479676717a1bfc263eb438e3e618e8c389
BLAKE2b-256 checksum
How to use checksums
be7c093c3e1f299fcdeecf6f2d84abe296b74bf312ec0be501165642bcb456f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Security for half-orm-litestar 1.0.0a5

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