Reliqua
A simple, efficient, and intuitive Python REST API framework built on Falcon and Gunicorn. Define resources with Sphinx-style docstrings and Reliqua handles routing, parameter validation, type coercion, and OpenAPI 3.1 documentation automatically.
Features
- Automatic route discovery — Resource modules are auto-loaded from a directory
- OpenAPI 3.1 generation — API docs generated from docstrings at startup
- Built-in Swagger UI — Interactive API explorer served at
/docs - Parameter validation & type coercion — Declared parameter types are automatically enforced
- Pluggable authentication & authorization — Basic, Cookie, Header, Query, and Bearer auth with role-based access control
- CORS support — Enabled by default
- Database helpers — MySQL, PostgreSQL, and SQLite via Peewee ORM
- Custom media handlers — JSON (with datetime support), YAML, and plain text
Installation
pip install reliqua
Dependencies
falcon>=3.0.0falcon-corsgunicorn>=19.6.0pyyaml
Optional for database support: peewee
Quick Start
1. Create a Resource
Resources are classes that subclass Resource. Routes, parameters, and responses are all defined via class attributes and docstrings.
from reliqua.resources.base import Resource
from reliqua.exceptions import HTTPNotFound
users = [
{"username": "ted", "email": "ted@nowhere.com"},
{"username": "bob", "email": "bob@nowhere.com"},
]
USER = {
"type": "object",
"properties": {
"username": {"type": "string", "examples": ["billy"]}
},
"required": ["username"],
}
USERS = {"type": "array", "items": {"$ref": "#/components/schemas/user"}}
class User(Resource):
"""User resource."""
__routes__ = {
"/users/{id}": {"suffix": "by_id"},
}
__tags__ = ["users"]
__auth__ = {"GET": ["admin"]}
user = USER
def on_get_by_id(self, req, resp, id=None):
"""
Return a user.
:param str id: [in=path required] User ID
:response 200 user: User was retrieved
:response 404: User not found
:return json:
"""
try:
resp.media = users[int(id)]
except IndexError as exc:
raise HTTPNotFound("User not found") from exc
class Users(Resource):
"""Users resource."""
__routes__ = {"/users": {}}
__tags__ = ["users"]
users = USERS
def on_get(self, req, resp):
"""
Return users.
:param str username: [in=query] Username
:param str email: [in=query default=ted@nowhere.com] Email
:response 200 users: Users were retrieved
:response 401: Invalid Authorization
:return json:
"""
resp.media = {"results": users}
def on_post(self, req, resp):
"""
Create a new user.
:param str username: [in=body required] Username
:param str email: [in=body required] Email
:accepts json:
:return json:
"""
users.append(req.params)
resp.media = len(users) - 1
2. Create the Application
from reliqua import Application
app = Application(
resource_path="/path/to/resources",
info={
"title": "My API",
"version": "1.0.0",
"description": "My awesome API",
},
gunicorn={
"bind": "127.0.0.1:8000",
"workers": 2,
},
)
app.run()
The OpenAPI UI will be available at:
http://127.0.0.1:8000/docs
The OpenAPI JSON spec will be available at:
http://127.0.0.1:8000/openapi/openapi.json
Configuration
The Application constructor accepts the following option groups:
info — API Metadata
| Key | Default | Description |
|---|---|---|
title |
"Application Title" |
API title shown in docs |
version |
"1.0.0" |
API version |
description |
"" |
API description |
license |
"" |
License name |
license_url |
"" |
License URL |
contact_name |
"" |
Contact name |
openapi — Documentation Options
| Key | Default | Description |
|---|---|---|
ui_url |
None |
Public URL for the API (e.g., behind a proxy). Defaults to http://{bind} |
highlight |
True |
Enable syntax highlighting in Swagger UI |
sort |
"alpha" |
Sort order for operations |
path |
"/openapi" |
Path for OpenAPI spec endpoint |
docs |
"/docs" |
Path for Swagger UI |
servers |
[] |
Additional server entries for OpenAPI spec |
gunicorn — Server Options
| Key | Default | Description |
|---|---|---|
bind |
"127.0.0.1:8000" |
Address and port to listen on |
workers |
1 |
Number of worker processes |
worker_class |
"gthread" |
Gunicorn worker class |
timeout |
30 |
Worker timeout in seconds |
keepalive |
2 |
Keep-alive timeout |
loglevel |
"critical" |
Log level |
accesslog |
"-" |
Access log target |
errorlog |
"-" |
Error log target |
Other Parameters
| Parameter | Default | Description |
|---|---|---|
resource_path |
"" |
Directory containing resource modules |
middleware |
[] |
List of middleware instances |
config |
None |
App config dict accessible on resources as self.app_config |
resource_attributes |
{} |
Key/value pairs set as attributes on every resource |
Resources
Routes
Routes are defined as a dictionary on the __routes__ class attribute. Keys are URL patterns and values are option dicts.
class MyResource(Resource):
__routes__ = {
"/items": {},
"/items/{id}": {"suffix": "by_id"},
}
def on_get(self, req, resp):
"""Handle GET /items."""
def on_get_by_id(self, req, resp, id=None):
"""Handle GET /items/{id}."""
When a resource has multiple routes, the suffix option distinguishes handler methods (e.g., on_get_by_id, on_delete_by_id).
Docstring Parameters
Parameters are declared using Sphinx-style docstrings:
:param <type> <name>: [<options>] <description>
Supported types: str, int, float, bool, list, list[int], list[str], list[dict], object, dict, json
Options (inside []):
| Option | Description |
|---|---|
in=query|path|body|form |
Parameter location (default: query) |
required |
Mark as required (path params are always required) |
default=<value> |
Default value |
enum=<attr> |
Resource attribute containing allowed values |
min=<n> |
Minimum value |
max=<n> |
Maximum value |
Responses
:response <code> [<schema>]: <description>
The optional <schema> name references a class attribute containing an OpenAPI schema dict. The schema is automatically added to the OpenAPI components/schemas section.
Content Types
:return [json, yaml, xml]: Response content types
:accepts [json, xml]: Accepted request content types
Supported aliases: json, yaml, xml, html, text, binary, gzip, form, jpeg, png, gif
Resource Attributes
| Attribute | Description |
|---|---|
__routes__ |
Dict mapping URL patterns to route options |
__tags__ |
List of OpenAPI tags (defaults to class name) |
__auth__ |
Dict mapping HTTP methods to allowed roles |
__components__ |
Dict of OpenAPI component schemas |
no_auth |
Set to True to bypass authentication |
Helper Methods
The Resource base class provides get_params(req, keys=None, exclude=None) to extract request parameters.
Authentication & Authorization
Reliqua separates authentication (identity verification) from authorization (access control).
Authentication
from reliqua.auth import (
AuthenticationContext,
AuthMiddleware,
BasicAuthentication,
CookieAuthentication,
HeaderAuthentication,
QueryAuthentication,
MultiAuthentication,
)
def validate_user(username, password):
if username == "admin" and password == "secret":
return AuthenticationContext(user="admin", role="admin")
return None
def validate_api_key(api_key):
if api_key == "abc123":
return AuthenticationContext(name="api_user", role="admin")
return None
# HTTP Basic auth
basic_auth = BasicAuthentication(validation=validate_user)
# API key from cookie
cookie_auth = CookieAuthentication("api_key", validation=validate_api_key)
# API key from header
header_auth = HeaderAuthentication("X-API-Key", validation=validate_api_key)
# API key from query parameter
query_auth = QueryAuthentication("api_key", validation=validate_api_key)
# Try multiple authenticators in order
multi_auth = MultiAuthentication([basic_auth, cookie_auth])
Validation callbacks receive credentials and must return an AuthenticationContext on success or a falsy value on failure.
Authorization
Four access control strategies are available:
AccessResource — Per-resource authorization via __auth__ class attributes:
from reliqua.auth import AccessResource, AuthMiddleware
control = AccessResource(default_mode="deny")
auth = AuthMiddleware([basic_auth], control=control)
class SecureResource(Resource):
__auth__ = {
"GET": ["admin", "user"],
"POST": ["admin"],
}
AccessMap — A centralized route-to-role mapping:
from reliqua.auth import AccessMap
access_map = {
"/users": {"GET": ["admin", "user"], "POST": ["admin"]},
"/public": {"*": ["*"]}, # wildcard: any role, any method
}
control = AccessMap(access_map)
AccessList — Simple allow/deny lists of routes and methods:
from reliqua.auth import AccessList
control = AccessList(
routes=["/secret"],
methods=["DELETE"],
default_mode="allow", # everything open except listed routes/methods
)
AccessCallback — Delegate to custom functions:
from reliqua.auth import AccessCallback
control = AccessCallback(
authenticate_callback=my_auth_check,
authorized_callback=my_role_check,
)
Applying Auth Middleware
auth_middleware = AuthMiddleware([basic_auth, cookie_auth], control=control)
app = Application(
resource_path="/path/to/resources",
middleware=[auth_middleware],
)
Resources with no_auth = True bypass authentication entirely. The authenticated context is available in handlers via req.context["authentication"].
Database Support
Database helpers use the Peewee ORM with a proxy pattern.
from reliqua.database import BaseModel, mysql_connect, DatabaseConnection
import peewee
# Connect to the database
db = mysql_connect("localhost", "mydb", "user", "password", 3306)
# Define a model
class UserModel(BaseModel):
username = peewee.CharField()
email = peewee.CharField()
# Add DatabaseConnection middleware to manage connections per request
app = Application(
resource_path="/path/to/resources",
middleware=[DatabaseConnection()],
)
Available connection helpers: mysql_connect(), psql_connect(), sqlite_connect()
Media Handlers
Built-in handlers are registered automatically:
| Content Type | Handler | Notes |
|---|---|---|
application/json |
JSONHandler |
Serializes datetime, date, and time objects |
application/yaml |
YAMLHandler |
YAML via PyYAML |
text/html |
TextHandler |
Plain text |
text/plain |
TextHandler |
Plain text |
Example Application
A full example application is included in the example/ directory demonstrating resources, authentication, form handling, and binary media streaming.
# Run the example
python -m example
# Custom bind address and port
python -m example --address 0.0.0.0 --port 9000
The Swagger UI will be available at http://127.0.0.1:8000/docs.
License
MIT
Metadata
Release files for reliqua 0.0.15
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| reliqua-0.0.15.tar.gz | 1.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| reliqua-0.0.15-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.1 MB
Release files / reliqua-0.0.15.tar.gz
| Download URL | reliqua-0.0.15.tar.gz |
|---|---|
| Size | 1.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b1082a354b185374ec98a196f41c6dfd40507b6ec7b86b060ded43acb4386872
|
|
BLAKE2b-256 checksum How to use checksums |
530b82418f58d85f23dbaa6625d55314173efcace2e0007d30891acce5ca0673
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.
Transparency logRelease files / reliqua-0.0.15-py3-none-any.whl
| Download URL | reliqua-0.0.15-py3-none-any.whl |
|---|---|
| Size | 1.5 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d1b07d7cf91a3bdb7d0e8b75e122ecc198a9f890275939b2e5f5786401ffa91c
|
|
BLAKE2b-256 checksum How to use checksums |
2df57eec4132c322f1e5b0c5b0586af9bf40aefadcd567a16fb346069d3a33a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.
Transparency log