{"id":50808744,"url":"https://github.com/wskr00/pyrsql","last_synced_at":"2026-06-13T03:08:42.013Z","repository":{"id":355497936,"uuid":"1226705764","full_name":"wskr00/pyrsql","owner":"wskr00","description":"Compiler-oriented RSQL query engine for safe, typed, and extensible Python ORM-backed APIs.","archived":false,"fork":false,"pushed_at":"2026-06-13T01:10:42.000Z","size":1415,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T02:21:45.294Z","etag":null,"topics":["fastapi","filter","pagination","python","rsql","sort","sqlalchemy"],"latest_commit_sha":null,"homepage":"https://wskr00.github.io/pyrsql/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wskr00.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T18:28:56.000Z","updated_at":"2026-06-13T01:07:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"e8236fee-d303-4f90-a964-72a31e7df51a","html_url":"https://github.com/wskr00/pyrsql","commit_stats":null,"previous_names":["wskr00/pyrsql"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/wskr00/pyrsql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wskr00%2Fpyrsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wskr00%2Fpyrsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wskr00%2Fpyrsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wskr00%2Fpyrsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wskr00","download_url":"https://codeload.github.com/wskr00/pyrsql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wskr00%2Fpyrsql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34270441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastapi","filter","pagination","python","rsql","sort","sqlalchemy"],"created_at":"2026-06-13T03:08:41.463Z","updated_at":"2026-06-13T03:08:41.991Z","avatar_url":"https://github.com/wskr00.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pyrsql\n\nA compiler-oriented RSQL query engine for safe, typed, and extensible\nfiltering, sorting, and pagination in Python APIs.\n\n[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14%20%7C%203.14t-blue?logo=python)](https://pypi.org/project/pyrsql/)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n\npyrsql compiles RSQL query strings into ORM-specific statement objects\nthrough a language frontend, semantic binding, and pluggable backend\nlowering - making it easy to expose complex query capabilities in your API\nwithout coupling to a specific ORM or framework.\n\n**Current backends:** SQLAlchemy 2.0  \n**Current framework adapters:** FastAPI  \n**Planned:** Django ORM, SQLModel, Flask\n\n## Why pyrsql?\n\nMost API filtering libraries are tightly coupled to one ORM or one framework.\npyrsql is built as a **compiler pipeline**. Parsing, semantic analysis, and\nbackend lowering are separate stages. Adding a new ORM backend means\nimplementing one interface (`ORM`), not rewriting the parser or query\nlanguage.\n\n- **ORM-neutral core** - `Query`, `Sort`, `PageRequest` have zero ORM dependencies\n- **Pluggable backends** - implement `compile_query` / `compile_sort` /\n  `compile_page_request` for any ORM\n- **Pluggable framework adapters** - FastAPI today, Flask/Django tomorrow\n- **Custom operators** - define your own RSQL operators with per-ORM lowering\n- **Field policies** - whitelist, blacklist, aliases at global and per-model level\n- **Type-safe** - strict mypy, Google-style docstrings, immutable value objects\n- **Performance-oriented internals** - immutable `msgspec` models across the\n  core pipeline and configuration objects\n- **Security-oriented request handling** - parser/sort limits, structural\n  allowlists/blocklists, sanitized FastAPI error payloads\n\n## Quickstart\n\n```bash\npip install pyrsql[sqlalchemy]\n```\n\n```python\nimport pyrsql\nfrom sqlalchemy import select\nfrom pyrsql.orms.sqlalchemy import SQLAlchemyORM\n\norm = SQLAlchemyORM()\nstmt = select(User)\n\n# Filter: name equals \"demo\", company name contains \"acme\"\nstmt = pyrsql.parse(\"name==demo;company.name==acme*\").apply(stmt, User, orm=orm)\n\n# Sort: by name ascending, then company name descending\nstmt = pyrsql.Sort.parse(\"name,asc;company.name,desc\").apply(stmt, User, orm=orm)\n\n# Paginate: page 0, 25 items per page\nstmt = pyrsql.PageRequest.of(0, 25).apply(stmt, User, orm=orm)\n```\n\n## Features\n\n### Query (filter)\n\n- 20+ built-in operators: `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=`, `=in=`, `=out=`, `=like=`, `=ilike=`, `=bt=`, `=na=`, `=nn=`, etc.\n- Logical composition: `;` (AND), `,` (OR)\n- Grouping with parentheses\n- Wildcard matching (`*demo*`) and case-insensitive markers (`^demo`)\n- Strict equality mode (literal `*` and `^`)\n- Configurable `LIKE` escape character\n- `SELECT DISTINCT` support\n- Configurable parser limits (query length, depth, argument count)\n\n### Sort\n\n- Multi-field: `name,asc;company.name,desc,ic`\n- Ignore-case modifier (`ic`)\n- Function selectors: `@upper[name],asc`\n\n### Pagination\n\n- Page-number + page-size API: `PageRequest.of(0, 25)`\n- Offset + limit API: `PageRequest.from_offset(offset=50, limit=25)`\n- Configurable max page size\n\n### Field Mapping \u0026 Access Control\n\n- Aliases: `field_mapping={\"username\": \"user.name\"}`\n- Whitelist/blacklist: `field_whitelist`, `field_blacklist`\n- Per-model policies: `model_field_mapping`, `model_field_whitelist`, `model_field_blacklist`\n\n### Custom Predicates\n\nDefine custom operators with ORM-specific lowering:\n\n```python\nfrom pyrsql import CustomPredicateDefinition, QueryOptions\nfrom pyrsql.parsing.operators import ComparisonOperator\n\nall_match = ComparisonOperator(name=\"all_match\", spellings=(\"=all=\",), ...)\noptions = QueryOptions(\n    custom_predicates={\n        \"all_match\": CustomPredicateDefinition(operator=all_match, argument_type=str),\n    },\n)\n```\n\n### Value Conversion\n\n- Built-in: `bool`, `int`, `float`, `Decimal`, `UUID`, `date`, `time`, `datetime`, `enum`\n- Custom converters: `ValueConverterRegistry` + `with_converter`\n- Field-scoped: `field_value_converters={\"created_at\": my_converter}`\n- Model-scoped: `model_field_value_converters={MyModel: {\"field\": my_converter}}`\n\n### Join Hints\n\n```python\nfrom pyrsql import QueryOptions\nfrom pyrsql.core.joins import JoinHint\n\noptions = QueryOptions(join_hints={\"User.company\": JoinHint.LEFT})\n```\n\n### PostgreSQL JSON / JSONB\n\n- **Whole-document**: direct `==`, `!=`, `=in=`, `=out=`, `=na=`, `=nn=` against JSONB columns\n- **Nested path**: `jsonb_path_exists` via PostgreSQL `jsonpath`\n- **Structured values**: arrays and objects passed as `jsonpath` vars\n- **Temporal**: `JSONOptions(use_datetime=True)` for datetime-aware `jsonpath`\n- **JSON sort**: text, integer, float, numeric, boolean, date, time, datetime\n- **Custom function names**: `JSONOptions(path_exists_function=...)`\n\n### FastAPI Integration\n\n```python\nfrom fastapi import Depends, FastAPI\nfrom pyrsql.adapters.fastapi import criteria_dependency\n\napp = FastAPI()\ndependency = criteria_dependency()\n\n@app.get(\"/items\")\ndef list_items(criteria = Depends(dependency)):\n    ...\n```\n\n- Auto-extracts `filter`, `sort`, `page`, `size` from query params\n- `HTTP 422` with structured diagnostics on parse/semantic errors\n- OpenAPI examples from configuration\n- One-based paging support\n- Custom query parameter names\n\n### FastAPI + SQLAlchemy Integration\n\n```python\nfrom pyrsql.integrations.fastapi import FastAPISQLAlchemyIntegration\n\nintegration = FastAPISQLAlchemyIntegration()\n\n@app.get(\"/users\")\ndef list_users(stmt = Depends(integration.select_dependency(User))):\n    return {\"sql\": str(stmt)}\n```\n\n- `select_dependency`, `count_select_dependency`, `paginated_select_dependency`\n- Declarative `resource()` with auto-generated OpenAPI examples\n- `applier_dependency` for custom base statements\n- Compatible with both sync `Session` and async `AsyncSession` execution\n- FastAPI parse and page-validation failures become structured `HTTP 400` payloads\n- FastAPI semantic and backend integration failures become structured `HTTP 422` payloads\n\n### Concurrency and Validation\n\n- Shared integration and ORM metadata caches are protected for free-threaded execution\n- Async support is validated for adapter, ORM, and integration flows\n- Dedicated async, free-threaded, and security test suites validate these flows\n\n## Documentation\n\nFull documentation at **[wskr00.github.io/pyrsql](https://wskr00.github.io/pyrsql/)**.\n\n| Section | Description |\n|---------|-------------|\n| [Quickstart](https://wskr00.github.io/pyrsql/quickstart/) | One-minute primer |\n| [Usage](https://wskr00.github.io/pyrsql/usage/query/) | Filter, sort, page, JSON, FastAPI, async flows, custom predicates |\n| [API Reference](https://wskr00.github.io/pyrsql/reference/api/) | Auto-generated from docstrings |\n| [Operators](https://wskr00.github.io/pyrsql/reference/operators/) | Complete operator table |\n| [Options](https://wskr00.github.io/pyrsql/reference/options/) | QueryOptions, SortOptions, JSONOptions |\n| [Architecture](https://wskr00.github.io/pyrsql/explanation/architecture/) | Pipeline, modules, design |\n| [Extensibility](https://wskr00.github.io/pyrsql/explanation/extensibility/) | Adding backends and adapters |\n| [Testing](https://wskr00.github.io/pyrsql/testing/) | Test layers, async, security, free-threaded validation |\n| [Contributing](https://wskr00.github.io/pyrsql/contributing/) | Setup, workflow, standards |\n\n## Development Principles\n\n- Object-oriented design\n- SOLID principles\n- Google Python Style Guide\n- Strong typing\n- ORM-neutral public API\n\n## License\n\n[MIT](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwskr00%2Fpyrsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwskr00%2Fpyrsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwskr00%2Fpyrsql/lists"}