{"id":13634320,"url":"https://github.com/thomaxxl/safrs","last_synced_at":"2025-10-06T00:55:27.974Z","repository":{"id":25517683,"uuid":"104184487","full_name":"thomaxxl/safrs","owner":"thomaxxl","description":"SqlAlchemy Flask-Restful Swagger Json:API OpenAPI","archived":false,"fork":false,"pushed_at":"2025-06-05T12:51:01.000Z","size":40895,"stargazers_count":411,"open_issues_count":5,"forks_count":73,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-08-26T07:51:38.836Z","etag":null,"topics":["flask","flask-restful","flask-sqlalchemy","json","json-api","openapi","openapi3","python3","rest-api","sqlalchemy","swagger"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomaxxl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-09-20T07:56:55.000Z","updated_at":"2025-08-17T20:16:47.000Z","dependencies_parsed_at":"2024-08-25T06:27:04.114Z","dependency_job_id":"f34a13a9-aace-45b0-a4fd-613f1a5a4580","html_url":"https://github.com/thomaxxl/safrs","commit_stats":{"total_commits":1210,"total_committers":17,"mean_commits":71.17647058823529,"dds":0.04132231404958675,"last_synced_commit":"d674affdc91f1e7f36e18312f42ef484c3205a52"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"purl":"pkg:github/thomaxxl/safrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomaxxl%2Fsafrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomaxxl%2Fsafrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomaxxl%2Fsafrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomaxxl%2Fsafrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomaxxl","download_url":"https://codeload.github.com/thomaxxl/safrs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomaxxl%2Fsafrs/sbom","scorecard":{"id":882633,"data":{"date":"2025-08-11","repo":{"name":"github.com/thomaxxl/safrs","commit":"ed561bd1551d9c64e0c22a8c13bec6e35a1b5ed2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"1 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":3,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Warn: no linked content found","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Code-Review","score":0,"reason":"Found 2/23 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Warn: no topLevel permission defined: .github/workflows/python-app.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/thomaxxl/safrs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/thomaxxl/safrs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/thomaxxl/safrs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:62: update your workflow using https://app.stepsecurity.io/secureworkflow/thomaxxl/safrs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-app.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/thomaxxl/safrs/python-app.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-app.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/thomaxxl/safrs/python-app.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/python-app.yml:25","Warn: pipCommand not pinned by hash: .github/workflows/python-app.yml:26","Warn: pipCommand not pinned by hash: .github/workflows/python-app.yml:27","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (10) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"39 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2018-66 / GHSA-562c-5r94-xh97","Warn: Project is vulnerable to: PYSEC-2019-179 / GHSA-5wv5-4vpf-pj6m","Warn: Project is vulnerable to: PYSEC-2023-62 / GHSA-m2qf-hxjv-5gpq","Warn: Project is vulnerable to: PYSEC-2018-54 / GHSA-894g-6j7q-2hx6","Warn: Project is vulnerable to: GHSA-43qf-4rqw-9q2g","Warn: Project is vulnerable to: GHSA-7rxf-gvfg-47g4","Warn: Project is vulnerable to: GHSA-84pr-m4jr-85g5","Warn: Project is vulnerable to: GHSA-8vgw-p6qm-5gr7","Warn: Project is vulnerable to: PYSEC-2024-71 / GHSA-hxwh-jpp2-84pm","Warn: Project is vulnerable to: PYSEC-2020-43 / GHSA-xc3p-ff3m-f46v","Warn: Project is vulnerable to: PYSEC-2019-217 / GHSA-462w-v97r-4m45","Warn: Project is vulnerable to: PYSEC-2014-8 / GHSA-8r7q-cvjq-x353","Warn: Project is vulnerable to: GHSA-cpwx-vrp4-4pq7","Warn: Project is vulnerable to: PYSEC-2014-82 / GHSA-fqh9-2qgg-h84h","Warn: Project is vulnerable to: PYSEC-2021-66 / GHSA-g3rq-g295-4j3m","Warn: Project is vulnerable to: GHSA-h5c8-rqwp-cp95","Warn: Project is vulnerable to: GHSA-h75v-3vvj-5mfj","Warn: Project is vulnerable to: PYSEC-2019-220 / GHSA-hj2j-77xm-mc5v","Warn: Project is vulnerable to: GHSA-q2x7-8rv6-6q7h","Warn: Project is vulnerable to: PYSEC-2018-67 / GHSA-9q2p-fj49-vpxj","Warn: Project is vulnerable to: GHSA-v9hf-5j83-6xpp","Warn: Project is vulnerable to: PYSEC-2021-142 / GHSA-8q59-q68h-6hv4","Warn: Project is vulnerable to: PYSEC-2018-49 / GHSA-rprw-h62v-c2w7","Warn: Project is vulnerable to: PYSEC-2019-124 / GHSA-38fc-9xqv-7f7q","Warn: Project is vulnerable to: PYSEC-2019-123 / GHSA-887w-45rq-vxgf","Warn: Project is vulnerable to: PYSEC-2012-9 / GHSA-hfg2-wf6j-x53p","Warn: Project is vulnerable to: GHSA-2g68-c3qc-8985","Warn: Project is vulnerable to: PYSEC-2020-157 / GHSA-3p3h-qghp-hvh2","Warn: Project is vulnerable to: GHSA-f9vj-2wh5-fj8j","Warn: Project is vulnerable to: PYSEC-2019-140 / GHSA-gq9m-qvpx-68hc","Warn: Project is vulnerable to: PYSEC-2017-43 / GHSA-h2fp-xgx6-xh6f","Warn: Project is vulnerable to: PYSEC-2023-221 / GHSA-hrfv-mqp8-q5rw","Warn: Project is vulnerable to: GHSA-j544-7q9p-6xp8","Warn: Project is vulnerable to: PYSEC-2023-57 / GHSA-px8h-6qxv-m22q","Warn: Project is vulnerable to: GHSA-q34m-jh98-gwm2","Warn: Project is vulnerable to: PYSEC-2023-58 / GHSA-xg9f-g7g7-2323","Warn: Project is vulnerable to: PYSEC-2022-203","Warn: Project is vulnerable to: GHSA-gmj6-6f8f-6699","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T08:48:32.384Z","repository_id":25517683,"created_at":"2025-08-24T08:48:32.384Z","updated_at":"2025-08-24T08:48:32.384Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278542671,"owners_count":26004061,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"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":["flask","flask-restful","flask-sqlalchemy","json","json-api","openapi","openapi3","python3","rest-api","sqlalchemy","swagger"],"created_at":"2024-08-01T23:01:01.773Z","updated_at":"2025-10-06T00:55:27.952Z","avatar_url":"https://github.com/thomaxxl.png","language":"Python","funding_links":[],"categories":["Third-Party Extensions","Flask Utilities","Python"],"sub_categories":["APIs"],"readme":"[![Latest Version](https://img.shields.io/pypi/v/safrs.svg)](https://pypi.python.org/pypi/safrs/)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/safrs.svg)](https://pypi.python.org/pypi/safrs/)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n![Python application](https://github.com/thomaxxl/safrs/workflows/Python%20application/badge.svg)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/de12c50717e8487db5dcc31907e627f7)](https://www.codacy.com/gh/thomaxxl/safrs/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=thomaxxl/safrs)\n[![Downloads](https://pepy.tech/badge/safrs)](https://pepy.tech/project/safrs)\n\n# SAFRS: Python OpenAPI \u0026 JSON:API Framework\n\n![demo](docs/images/safrs.gif)\n\n\u003ca class=\"mk-toclify\" id=\"table-of-contents\"\u003e\u003c/a\u003e\n\n- [Introduction](#overview)\n- [Installation](#installation)\n- [JSON:API Interface](#http-methods)\n- [Resource Objects](#resource-objects)\n- [Relationships](#relationships)\n- [Methods](#methods)\n    - [Custom Methods](#custom-methods)\n    - [Class Methods](#class-methods)\n- [Initialization](#initialization)\n- [Endpoint Naming](#endpoint-naming)\n- [Configuration](#configuration)\n- [Exposing Existing Databases](#expose-existing)\n- [More Examples and Use Cases](#more-examples-and-use-cases)\n- [Advanced Functionality](#advanced-usage)\n    - [Filtering](#filtering)\n- [Customization](#customization)\n    - [Custom Serialization](#custom-serialization)\n    - [Excluding Attributes and Relationships](#excluding-attrs-rels)\n    - [HTTP Decorators](#http-decorators)\n    - [API Methods](#api-methods)\n    - [Custom Swagger](#custom-swagger)\n    - [Classes Without SQLAlchemy Models](#Classes-Without-Models)\n\n\u003ca class=\"mk-toclify\" id=\"overview\"\u003e\u003c/a\u003e\n## Introduction\n\nSAFRS exposes SQLAlchemy database models as a [JSON:API](https://jsonapi.org) webservice and generates the corresponding [swagger/OpenAPI](https://swagger.io/).\n\nDocumentation can be found in the [wiki](https://github.com/thomaxxl/safrs/wiki).\n\n__We created a service to generate APIs using GenAI__, check it out [HERE](https://apifabric.ai)!\n\n__A [LIVE DEMO](https://thomaxxl.pythonanywhere.com) is available__, where much of the basic functionality is implemented using a simple [example](examples/demo_pythonanywhere_com.py).\n\n\n\u003ca class=\"mk-toclify\" id=\"installation\"\u003e\u003c/a\u003e\n## Installation\n\nSAFRS can be installed as a [pip package](https://pypi.python.org/pypi/safrs/) or by downloading the latest version from github, for example:\n\n```bash\ngit clone https://github.com/thomaxxl/safrs\ncd safrs\npip install .\n```\n\nOnce the dependencies are installed, the [examples](examples) can be started, for example\n```\npython examples/demo_relationship.py \"your-interface-ip\"\n```\n\n\u003ca class=\"mk-toclify\" id=\"http-methods\"\u003e\u003c/a\u003e\n## JSON:API Interface\n\nExposed resource objects can be queried using the [JSON:API format](http://jsonapi.org/format/). The API supports following HTTP operations:\n\n- GET : Retrieve an object or a list of objects\n- PATCH : Update an object.\n- DELETE: Delete an object.\n- POST : Create an object. \n\nPlease check the [JSON:API spec](http://jsonapi.org/format/) for more implementation details. \nYou can also try out the interface in the [live demo](http://thomaxxl.pythonanywhere.com/api).\n\n\u003ca class=\"mk-toclify\" id=\"resource-objects\"\u003e\u003c/a\u003e\n## Resource Objects\n\nDatabase objects are implemented as subclasses of the SAFRSBase and SQLAlchemy model classes. The SQLAlchemy columns are serialized to JSON when the corresponding REST API is invoked. \n\nFollowing example [app](examples/demo_relationship.py) illustrates how the API is built and documented:\n\n```python\nclass User(SAFRSBase, db.Model):\n    \"\"\"\n        description: User description\n    \"\"\"\n\n    __tablename__ = \"Users\"\n    id = db.Column(db.Integer, primary_key=True)\n    name = db.Column(db.String)\n    email = db.Column(db.String)\n\n```\n\nThe User class is implemented as a subclass of \n- db.Model: SQLAlchemy base\n- SAFRSBase: Implements JSON serialization for the object and generates (swagger) API documentation\n\nThis User object is then exposed through the web interface using the Api object\n\n```python \napi.expose_object(User)\n``` \n\nThe User object REST methods are available on /User, the swagger schema is available on /api/swagger.json and the UI is available on /api/:\n![User Swagger](docs/images/USER_swagger.png)\n\n\n\u003ca class=\"mk-toclify\" id=\"relationships\"\u003e\u003c/a\u003e\n## Relationships\n\nDatabase object such as the User class from the demo.py example can be extended to include relationships with other objects. The demo_relationship.py contains following extension of the User class where a relationship with the Book class is implemented:\n\n```python\nclass User(SAFRSBase, db.Model):\n    '''\n        description: User description\n    '''\n    __tablename__ = 'Users'\n    id = db.Column(db.String, primary_key=True)\n    name = db.Column(db.String, default='')\n    email = db.Column(db.String, default='')\n    books = db.relationship('Book', back_populates=\"user\")\n...\n``` \n\nA many-to-one database association is declared by the back_populates relationship argument.\nThe Book class is simply another subclass of SAFRSBase and db.Model, similar to the previous User class:\n\n```python\nclass Book(SAFRSBase, db.Model):\n    '''\n        description: Book description\n    '''\n    __tablename__ = 'Books'\n    id = db.Column(db.String, primary_key=True)\n    name = db.Column(db.String, default='')\n    user_id = db.Column(db.String, db.ForeignKey('Users.id'))\n    user = db.relationship('User', back_populates='books')\n```\n\nThe User.book relationship can be queried in the API through the following endpoints:\n![Relations Swagger](docs/images/Relations_swagger.png)\n\n- POST adds an item to the relationship\n- DELETE removes an item from the relationship\n- GET retrieves a list of item ids\n\nThe relationship API endpoints work similarly for one-to-many relationships.\n\nRelationship members can also be included in the response when querying an instance, by specifying the relationship names as a comma separated list in the `include` query argument.\n\n![relationship include swagger](docs/images/rel_include.PNG)\n\nFor example, to retrieve all items in the `books_read` relationship from the People endpoint, you may add the `include=books_read` url parameter\n\n\nhttp://thomaxxl.pythonanywhere.com/api/People/?include=books_read\n\nTo retrieve nested relationship items, you can specify the nested relationship name after the '.', to retrieve the authors of the books_read instances for instance, you can use\n\nhttp://thomaxxl.pythonanywhere.com/api/People/?include=books_read.author\n\n\u003ca class=\"mk-toclify\" id=\"methods\"\u003e\u003c/a\u003e\n## Methods\n\n\u003ca class=\"mk-toclify\" id=\"custom-methods\"\u003e\u003c/a\u003e\n### Custom Methods\n\nSafrs allows the user to implement custom methods on the exposed objects. This methods can be invoked through the json API by sending an HTTP POST request to the method endpoint\nThe following example implements a \"send_mail\" method fro example:\n\n```python\nclass User(SAFRSBase, db.Model):\n    '''\n        description: User description\n    '''\n    __tablename__ = 'Users'\n    id = Column(String, primary_key=True)\n    name = Column(String, default='')\n    email = Column(String, default='')\n\n    # Following method is exposed through the REST API \n    # This means it can be invoked with a HTTP POST\n    @jsonapi_rpc(http_methods=['POST','GET'])\n    def send_mail(self, email):\n        '''\n            description : Send an email\n            args:\n                email:\n                    type : string \n                    example : test email\n        '''\n        content = 'Mail to {} : {}\\n'.format(self.name, email)\n        return { 'result' : 'sent {}'.format(content)}\n\n```\n\nThis method shows up in the swagger interface:\n\n![Method Swagger](docs/images/method_swagger.PNG)\n\nThe ```send_mail``` method is documented with the ```jsonapi_rpc``` decorator. \nThis decorator generates a schema based on the function documentation. This documentation contains yaml specification of the API which is used by the swagger UI. \n\n[api_methods.py](safrs/api_methods.py) contains a couple of methods that can be used in your models.\n\nThe yaml specification has to be in the first part of the function and class comments. These parts are delimited by four dashes (\"----\") . The rest of the comment may contain additional documentation.\n\n\u003ca class=\"mk-toclify\" id=\"class-methods\"\u003e\u003c/a\u003e\n### Class Methods\n\nTwo class-level methods have been defined to facilitate object retrieval:\n\n* **lookup** : retrieve a list of objects that match the argument list. For example, following HTTP POST request to a container will retrieve a list of itemswhere the name is \"thomas\"\n```json\n{\n  \"method\": \"lookup\",\n  \"args\": {\n    \"name\": \"thomas\"\n  }\n}\n```\n\n* **get_list** : retrieve a list of the items with the specified ID's\n\n\u003ca class=\"mk-toclify\" id=\"initialization\"\u003e\u003c/a\u003e\n## Application Initialization\nThe API can be initialized like this:\n```python\napi = SafrsApi(app, host=HOST, port=PORT, prefix=API_PREFIX)\n```\nThen you can expose objects with `expose_object`\n```python\napi.expose_object(User)    \n```\n\nAn example that uses the flask app factory pattern is implement in [examples/mini_app.py](examples/mini_app.py)\n\n\u003ca class=\"mk-toclify\" id=\"endpoint-naming\"\u003e\u003c/a\u003e\n## Endpoint Naming\nAs can be seen in the swagger UI:\n- the endpoint collection path names are the SQLAlchemy \\_\\_tablename\\_\\_ properties (e.g. /Users )\n- the parameter names are derived from the SAFRSBase class names (e.g. {UserId} )\n- the the relationship names are the SAFRSBase class relationship names (e.g /books )\nThe URL path format is [configurable](#configuration)\n\n\u003ca class=\"mk-toclify\" id=\"configuration\"\u003e\u003c/a\u003e\n## Configuration\n\nSome configuration parameters can be set in [config.py](safrs/config.py):\n- USE_API_METHODS: set this to false in case you want to disable the `jsonapi_rpc` functionality\n- INSTANCE_URL_FMT: This parameter declares the instance url path format\n- RELATIONSHIP_URL_FMT: This parameter declares the relationship endpoint path format\n\n\u003ca class=\"mk-toclify\" id=\"expose-existing\"\u003e\u003c/a\u003e\n## Exposing Existing Databases\nSafrs allows you to Expose existing databases as jsona:api services with the [expose_existing.py](expose_existing/expose_existing.py) script, for example:\n\n```bash\npython3 expose_existing.py mysql+pymysql://root:password@localhost/sakila --host localhost\n```\n\nThis script uses sqlacodegen to generate a source file containing the SQLAlchemy and `SAFRSBase` database models and starts the API webservice.\n\nMore details [here](docs/ExposeDB.md). This approach is used by the [ApiLogicServer](https://github.com/valhuber/ApiLogicServer) project.\n\n\u003ca class=\"mk-toclify\" id=\"more-examples-and-use-cases\"\u003e\u003c/a\u003e\n## More Examples and Use Cases\nThe [examples](examples) folder contains more example scripts:\n- Using a sha hash as primary key (id)\n- CORS usage\n- Flask-Admin integration example, eg.:\n![demo](docs/images/flask-admin.png)\n\nA docker image can be found here:\n[https://github.com/thomaxxl/safrs-example](https://github.com/thomaxxl/safrs-example)\n\n\u003ca class=\"mk-toclify\" id=\"advanced-usage\"\u003e\u003c/a\u003e\n## Advanced Functionality\n\n\u003ca class=\"mk-toclify\" id=\"filtering\"\u003e\u003c/a\u003e\n### Filtering\nThe swagger shows the jsonapi filters that can be used in the url query arguments. Items with an exact match of the specified attribute value can be fetched by specifying the corresponding key-value query parameter. For example, suppose the `User` class, exposed at `/Users` has a `name` attribute, to retrieve all instances with the name \"John\", you can use a `GET` request to \n`/Users?filter[name]=John`.\n\nIt is also possible to use more generic filters by specifiying a JSON string, for example `filter=[{\"name\":\"timestamp\",\"op\":\"gt\",\"val\":\"2020-08-01\"},{\"name\":\"timestamp\",\"op\":\"lt\",\"val\":\"2020-08-02\"}]`.\n\nMore info can be found in the [wiki](https://github.com/thomaxxl/safrs/wiki/API-Functionality#filtering).\n\n\u003ca class=\"mk-toclify\" id=\"custom-serialization\"\u003e\u003c/a\u003e\n### Custom Serialization\nSerialization and deserialization are implemented by the SAFRSBase `to_dict` and `__init__` : you can extend these methods as usual.\nFor example, if you would like to add some attributes to the json payload of the User object, you can override the to_dict method:\n\n```python\nclass User(SAFRSBase, db.Model):\n    '''\n        description: User description\n    '''\n    __tablename__ = 'Users'\n    id = db.Column(db.String, primary_key=True)\n    name = db.Column(db.String, default='')\n    email = db.Column(db.String, default='')\n    books = db.relationship('Book', back_populates=\"user\")\n\n    def to_dict(self):\n        result = SAFRSBase.to_dict(self)\n        result['custom_field'] = 'custom'\n        return result\n``` \n\nThis will add the `custom_field` attribute to the result attributes:\n```json\n\"attributes\": {\n    \"custom_field\": \"custom\",\n    \"email\": \"reader_email0\",\n    \"name\": \"Reader 0\"\n}\n```\n\n\u003ca class=\"mk-toclify\" id=\"customization\"\u003e\u003c/a\u003e\n## Customization\n\n\u003ca class=\"mk-toclify\" id=\"excluding-attrs-rels\"\u003e\u003c/a\u003e\n### Excluding Attributes and Relationships\nIt is possible to specify attributes and relationships that should not be serialized by specifying the respective `exclude_attrs` and èxclude_rels` class attributes in your SAFRSBase instances.\nExamples can be found [here](examples/demo_pythonanywhere_com.py#L81) and [here](examples/demo_http_get.py#L21)\n\n\u003ca class=\"mk-toclify\" id=\"limit-http-verbs\"\u003e\u003c/a\u003e\n### Limiting HTTP Methods\nIt is possible to limit the HTTP methods that are allowed by overriding the `http_methods` class attribute. An example can be found [here](examples/demo_http_get.py#L20)\n\n\u003ca class=\"mk-toclify\" id=\"HTTP-decorators\"\u003e\u003c/a\u003e\n### HTTP Decorators\nThe `decorators` class attribute list can be used to add custom decorators to the HTTP endpoints. An example of this functionality is implemented\nin the [authentication examples](examples/authentication).\n\n\n\u003ca class=\"mk-toclify\" id=\"api-methods\"\u003e\u003c/a\u003e\n### API Methods\nSome additional API RPC methods are implemented in [api_methods.py](safrs/api_methods.py), e.g. mysql regex search. \n\n\u003ca class=\"mk-toclify\" id=\"custom-swagger\"\u003e\u003c/a\u003e\n### Custom swagger\nThe swagger schema can be merged with a modified schema dictionary by supplying the to-be-merged dictionary as the `custom_swagger` argument to `SafrsApi`, e.g.\n```python\ncustom_swagger = {\"info\": {\"title\" : \"New Title\" }} # Customized swagger title will be merged\napi = SafrsApi(app, host=swagger_host, port=PORT, prefix=OAS_PREFIX, api_spec_url=OAS_PREFIX+'/swagger',\n               custom_swagger=custom_swagger, schemes=['http', 'https'], description=description)\n```\n\n\u003ca class=\"mk-toclify\" id=\"Classes-Without-Models\"\u003e\u003c/a\u003e\n### Classes Without SQLAlchemy Models\nYou can implement a serializable class without a model but this requires some extra work because safrs needs to know which attributes and relationships to serialize. An example is implemented [here](examples/demo_stateless.py)\n\n### More Customization\nThe documentation is being moved to the [wiki](https://github.com/thomaxxl/safrs/wiki)\n\n\u003cdetails\u003e\n\u003csummary\u003eAbout\u003c/summary\u003e\nSAFRS is an acronym for **S**ql**A**lchemy **F**lask-**R**estful **S**wagger. The purpose of this framework is to help python developers create a self-documenting JSON API for sqlalchemy database objects and relationships. These objects can be serialized to JSON and can be created, retrieved, updated and deleted through the JSON API. \nOptionally, custom resource object methods can be exposed and invoked using JSON.\nClass and method descriptions and examples can be provided in yaml syntax in the code comments. The description is parsed and shown in the swagger web interface. \n\nThe result is an easy-to-use [swagger/OpenAPI](https://swagger.io/) and [JSON:API](https://jsonapi.org) compliant API implementation.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003elimitations \u0026 Todos\u003c/summary\u003e\nThis code was developed for a specific use-case and may not be flexible enough for everyone's needs. A lot of the functionality is available but not documented for the sake of brevity.\nPerformance is reasonable for regular databases, but once you start exposing really big tables you may run into problems, for example: the `count()` for mysql innodb is slow on large(1M rows) tables, a workaround can be implemented by querying the `sys` tables or using werkzeug caching. \nFeel free to open an issue or drop [me](mailto:thomas.pollet+no+spam+@gmail.com) an email if you run into problems or something isn't clear!\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eReferences\u003c/summary\u003e\n\n- [JSON:API specification](http://jsonapi.org/format/)\n- [OpenApi (Swagger)](https://www.openapis.org/)\n- [Flask](http://flask.pocoo.org/)\n- [SQLAlchemy](https://www.sqlalchemy.org/)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eThanks\u003c/summary\u003e\nI developed this code when I worked at [Excellium Services](https://www.excellium-services.com/). They allowed me to open source it when I stopped working there.\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomaxxl%2Fsafrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomaxxl%2Fsafrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomaxxl%2Fsafrs/lists"}