FastAPI-JSONAPI


NameFastAPI-JSONAPI JSON
Version 2.8.0 PyPI version JSON
download
home_pageNone
SummaryFastAPI extension to create REST web api according to JSON:API 1.0 specification with FastAPI, Pydantic and data provider of your choice (SQLAlchemy, Tortoise ORM)
upload_time2024-04-27 13:27:10
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseNone
keywords fastapi json:api jsonapi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            [![Last Commit](https://img.shields.io/github/last-commit/mts-ai/FastAPI-JSONAPI?style=for-the-badge)](https://github.com/mts-ai/FastAPI-JSONAPI)
[![PyPI](https://img.shields.io/pypi/v/fastapi-jsonapi?label=PyPI&style=for-the-badge)](https://pypi.org/project/FastAPI-JSONAPI/)
[![](https://img.shields.io/pypi/pyversions/FastAPI-JSONAPI?style=for-the-badge)](https://pypi.org/project/FastAPI-JSONAPI/)
[![](https://img.shields.io/github/license/ycd/manage-fastapi?style=for-the-badge)](https://pypi.org/project/FastAPI-JSONAPI/)
[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/mts-ai/FastAPI-JSONAPI/testing.yml?style=for-the-badge)](https://github.com/mts-ai/FastAPI-JSONAPI/actions)
[![Read the Docs](https://img.shields.io/readthedocs/fastapi-jsonapi?style=for-the-badge)](https://fastapi-jsonapi.readthedocs.io/en/latest/)
[![Codecov](https://img.shields.io/codecov/c/github/mts-ai/FastAPI-JSONAPI?style=for-the-badge)](https://codecov.io/gh/mts-ai/FastAPI-JSONAPI)

[![📖 Docs (gh-pages)](https://github.com/mts-ai/FastAPI-JSONAPI/actions/workflows/documentation.yaml/badge.svg)](https://mts-ai.github.io/FastAPI-JSONAPI/)

# FastAPI-JSONAPI

FastAPI-JSONAPI is a FastAPI extension for building REST APIs.
Implementation of a strong specification [JSONAPI 1.0](http://jsonapi.org/).
This framework is designed to quickly build REST APIs and fit the complexity
of real life projects with legacy data and multiple data storages.

## Architecture

![docs/img/schema.png](docs/img/schema.png)

## Install

```bash
pip install FastAPI-JSONAPI
```

## A minimal API

Create a test.py file and copy the following code into it

```python
from pathlib import Path
from typing import Any, ClassVar, Dict

import uvicorn
from fastapi import APIRouter, Depends, FastAPI
from sqlalchemy import Column, Integer, Text
from sqlalchemy.engine import make_url
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

from fastapi_jsonapi import RoutersJSONAPI, init
from fastapi_jsonapi.misc.sqla.generics.base import DetailViewBaseGeneric, ListViewBaseGeneric
from fastapi_jsonapi.schema_base import BaseModel
from fastapi_jsonapi.views.utils import HTTPMethod, HTTPMethodConfig
from fastapi_jsonapi.views.view_base import ViewBase

CURRENT_FILE = Path(__file__).resolve()
CURRENT_DIR = CURRENT_FILE.parent
DB_URL = f"sqlite+aiosqlite:///{CURRENT_DIR}/db.sqlite3"

Base = declarative_base()


class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(Text, nullable=True)


class UserAttributesBaseSchema(BaseModel):
    name: str

    class Config:
        """Pydantic schema config."""

        orm_mode = True


class UserSchema(UserAttributesBaseSchema):
    """User base schema."""


class UserPatchSchema(UserAttributesBaseSchema):
    """User PATCH schema."""


class UserInSchema(UserAttributesBaseSchema):
    """User input schema."""


def async_session() -> sessionmaker:
    engine = create_async_engine(url=make_url(DB_URL))
    _async_session = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
    return _async_session


class Connector:
    @classmethod
    async def get_session(cls):
        """
        Get session as dependency

        :return:
        """
        sess = async_session()
        async with sess() as db_session:  # type: AsyncSession
            yield db_session
            await db_session.rollback()


async def sqlalchemy_init() -> None:
    engine = create_async_engine(url=make_url(DB_URL))
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)


class SessionDependency(BaseModel):
    session: AsyncSession = Depends(Connector.get_session)

    class Config:
        arbitrary_types_allowed = True


def session_dependency_handler(view: ViewBase, dto: SessionDependency) -> Dict[str, Any]:
    return {
        "session": dto.session,
    }


class UserDetailView(DetailViewBaseGeneric):
    method_dependencies: ClassVar[Dict[HTTPMethod, HTTPMethodConfig]] = {
        HTTPMethod.ALL: HTTPMethodConfig(
            dependencies=SessionDependency,
            prepare_data_layer_kwargs=session_dependency_handler,
        )
    }


class UserListView(ListViewBaseGeneric):
    method_dependencies: ClassVar[Dict[HTTPMethod, HTTPMethodConfig]] = {
        HTTPMethod.ALL: HTTPMethodConfig(
            dependencies=SessionDependency,
            prepare_data_layer_kwargs=session_dependency_handler,
        )
    }


def add_routes(app: FastAPI):
    tags = [
        {
            "name": "User",
            "description": "",
        },
    ]

    router: APIRouter = APIRouter()
    RoutersJSONAPI(
        router=router,
        path="/users",
        tags=["User"],
        class_detail=UserDetailView,
        class_list=UserListView,
        schema=UserSchema,
        resource_type="user",
        schema_in_patch=UserPatchSchema,
        schema_in_post=UserInSchema,
        model=User,
    )

    app.include_router(router, prefix="")
    return tags


def create_app() -> FastAPI:
    """
    Create app factory.

    :return: app
    """
    app = FastAPI(
        title="FastAPI and SQLAlchemy",
        debug=True,
        openapi_url="/openapi.json",
        docs_url="/docs",
    )
    add_routes(app)
    app.on_event("startup")(sqlalchemy_init)
    init(app)
    return app


app = create_app()

if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=8080,
        reload=True,
        app_dir=str(CURRENT_DIR),
    )
```

This example provides the following API structure:

| URL               | method | endpoint    | Usage                     |
|-------------------|--------|-------------|---------------------------|
| `/users`          | GET    | user_list   | Get a collection of users |
| `/users`          | POST   | user_list   | Create a user             |
| `/users`          | DELETE | user_list   | Delete users              |
| `/users/{obj_id}` | GET    | user_detail | Get user details          |
| `/users/{obj_id}` | PATCH  | user_detail | Update a user             |
| `/users/{obj_id}` | DELETE | user_detail | Delete a user             |

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "FastAPI-JSONAPI",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "fastapi, json:api, jsonapi",
    "author": null,
    "author_email": "Aleksey Nekrasov <a.nekrasov@mts.ai>, Suren Khorenyan <s.khorenyan@mts.ai>, German Bernadskiy <german11235813@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/7c/e3/d9f1a13b79d2871afa330d54ee33572278328e5dd95e8fadcf2ee5793e5d/fastapi_jsonapi-2.8.0.tar.gz",
    "platform": null,
    "description": "[![Last Commit](https://img.shields.io/github/last-commit/mts-ai/FastAPI-JSONAPI?style=for-the-badge)](https://github.com/mts-ai/FastAPI-JSONAPI)\n[![PyPI](https://img.shields.io/pypi/v/fastapi-jsonapi?label=PyPI&style=for-the-badge)](https://pypi.org/project/FastAPI-JSONAPI/)\n[![](https://img.shields.io/pypi/pyversions/FastAPI-JSONAPI?style=for-the-badge)](https://pypi.org/project/FastAPI-JSONAPI/)\n[![](https://img.shields.io/github/license/ycd/manage-fastapi?style=for-the-badge)](https://pypi.org/project/FastAPI-JSONAPI/)\n[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/mts-ai/FastAPI-JSONAPI/testing.yml?style=for-the-badge)](https://github.com/mts-ai/FastAPI-JSONAPI/actions)\n[![Read the Docs](https://img.shields.io/readthedocs/fastapi-jsonapi?style=for-the-badge)](https://fastapi-jsonapi.readthedocs.io/en/latest/)\n[![Codecov](https://img.shields.io/codecov/c/github/mts-ai/FastAPI-JSONAPI?style=for-the-badge)](https://codecov.io/gh/mts-ai/FastAPI-JSONAPI)\n\n[![\ud83d\udcd6 Docs (gh-pages)](https://github.com/mts-ai/FastAPI-JSONAPI/actions/workflows/documentation.yaml/badge.svg)](https://mts-ai.github.io/FastAPI-JSONAPI/)\n\n# FastAPI-JSONAPI\n\nFastAPI-JSONAPI is a FastAPI extension for building REST APIs.\nImplementation of a strong specification [JSONAPI 1.0](http://jsonapi.org/).\nThis framework is designed to quickly build REST APIs and fit the complexity\nof real life projects with legacy data and multiple data storages.\n\n## Architecture\n\n![docs/img/schema.png](docs/img/schema.png)\n\n## Install\n\n```bash\npip install FastAPI-JSONAPI\n```\n\n## A minimal API\n\nCreate a test.py file and copy the following code into it\n\n```python\nfrom pathlib import Path\nfrom typing import Any, ClassVar, Dict\n\nimport uvicorn\nfrom fastapi import APIRouter, Depends, FastAPI\nfrom sqlalchemy import Column, Integer, Text\nfrom sqlalchemy.engine import make_url\nfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import sessionmaker\n\nfrom fastapi_jsonapi import RoutersJSONAPI, init\nfrom fastapi_jsonapi.misc.sqla.generics.base import DetailViewBaseGeneric, ListViewBaseGeneric\nfrom fastapi_jsonapi.schema_base import BaseModel\nfrom fastapi_jsonapi.views.utils import HTTPMethod, HTTPMethodConfig\nfrom fastapi_jsonapi.views.view_base import ViewBase\n\nCURRENT_FILE = Path(__file__).resolve()\nCURRENT_DIR = CURRENT_FILE.parent\nDB_URL = f\"sqlite+aiosqlite:///{CURRENT_DIR}/db.sqlite3\"\n\nBase = declarative_base()\n\n\nclass User(Base):\n    __tablename__ = \"users\"\n    id = Column(Integer, primary_key=True, autoincrement=True)\n    name = Column(Text, nullable=True)\n\n\nclass UserAttributesBaseSchema(BaseModel):\n    name: str\n\n    class Config:\n        \"\"\"Pydantic schema config.\"\"\"\n\n        orm_mode = True\n\n\nclass UserSchema(UserAttributesBaseSchema):\n    \"\"\"User base schema.\"\"\"\n\n\nclass UserPatchSchema(UserAttributesBaseSchema):\n    \"\"\"User PATCH schema.\"\"\"\n\n\nclass UserInSchema(UserAttributesBaseSchema):\n    \"\"\"User input schema.\"\"\"\n\n\ndef async_session() -> sessionmaker:\n    engine = create_async_engine(url=make_url(DB_URL))\n    _async_session = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)\n    return _async_session\n\n\nclass Connector:\n    @classmethod\n    async def get_session(cls):\n        \"\"\"\n        Get session as dependency\n\n        :return:\n        \"\"\"\n        sess = async_session()\n        async with sess() as db_session:  # type: AsyncSession\n            yield db_session\n            await db_session.rollback()\n\n\nasync def sqlalchemy_init() -> None:\n    engine = create_async_engine(url=make_url(DB_URL))\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n\n\nclass SessionDependency(BaseModel):\n    session: AsyncSession = Depends(Connector.get_session)\n\n    class Config:\n        arbitrary_types_allowed = True\n\n\ndef session_dependency_handler(view: ViewBase, dto: SessionDependency) -> Dict[str, Any]:\n    return {\n        \"session\": dto.session,\n    }\n\n\nclass UserDetailView(DetailViewBaseGeneric):\n    method_dependencies: ClassVar[Dict[HTTPMethod, HTTPMethodConfig]] = {\n        HTTPMethod.ALL: HTTPMethodConfig(\n            dependencies=SessionDependency,\n            prepare_data_layer_kwargs=session_dependency_handler,\n        )\n    }\n\n\nclass UserListView(ListViewBaseGeneric):\n    method_dependencies: ClassVar[Dict[HTTPMethod, HTTPMethodConfig]] = {\n        HTTPMethod.ALL: HTTPMethodConfig(\n            dependencies=SessionDependency,\n            prepare_data_layer_kwargs=session_dependency_handler,\n        )\n    }\n\n\ndef add_routes(app: FastAPI):\n    tags = [\n        {\n            \"name\": \"User\",\n            \"description\": \"\",\n        },\n    ]\n\n    router: APIRouter = APIRouter()\n    RoutersJSONAPI(\n        router=router,\n        path=\"/users\",\n        tags=[\"User\"],\n        class_detail=UserDetailView,\n        class_list=UserListView,\n        schema=UserSchema,\n        resource_type=\"user\",\n        schema_in_patch=UserPatchSchema,\n        schema_in_post=UserInSchema,\n        model=User,\n    )\n\n    app.include_router(router, prefix=\"\")\n    return tags\n\n\ndef create_app() -> FastAPI:\n    \"\"\"\n    Create app factory.\n\n    :return: app\n    \"\"\"\n    app = FastAPI(\n        title=\"FastAPI and SQLAlchemy\",\n        debug=True,\n        openapi_url=\"/openapi.json\",\n        docs_url=\"/docs\",\n    )\n    add_routes(app)\n    app.on_event(\"startup\")(sqlalchemy_init)\n    init(app)\n    return app\n\n\napp = create_app()\n\nif __name__ == \"__main__\":\n    uvicorn.run(\n        \"main:app\",\n        host=\"0.0.0.0\",\n        port=8080,\n        reload=True,\n        app_dir=str(CURRENT_DIR),\n    )\n```\n\nThis example provides the following API structure:\n\n| URL               | method | endpoint    | Usage                     |\n|-------------------|--------|-------------|---------------------------|\n| `/users`          | GET    | user_list   | Get a collection of users |\n| `/users`          | POST   | user_list   | Create a user             |\n| `/users`          | DELETE | user_list   | Delete users              |\n| `/users/{obj_id}` | GET    | user_detail | Get user details          |\n| `/users/{obj_id}` | PATCH  | user_detail | Update a user             |\n| `/users/{obj_id}` | DELETE | user_detail | Delete a user             |\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "FastAPI extension to create REST web api according to JSON:API 1.0 specification with FastAPI, Pydantic and data provider of your choice (SQLAlchemy, Tortoise ORM)",
    "version": "2.8.0",
    "project_urls": {
        "Documentation": "https://fastapi-jsonapi.readthedocs.io/",
        "Source": "https://github.com/mts-ai/FastAPI-JSONAPI"
    },
    "split_keywords": [
        "fastapi",
        " json:api",
        " jsonapi"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7a6c1c1a7142b69662b802e511c4318d931adbf539827511f62cdb0fc8f7e609",
                "md5": "ea365c206ab738b01a5c6e06202e3396",
                "sha256": "a8adc94045fe1742577347e32d4fa0352941ce728b6a65b4483a3e2b99190583"
            },
            "downloads": -1,
            "filename": "fastapi_jsonapi-2.8.0-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ea365c206ab738b01a5c6e06202e3396",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 69777,
            "upload_time": "2024-04-27T13:27:08",
            "upload_time_iso_8601": "2024-04-27T13:27:08.649160Z",
            "url": "https://files.pythonhosted.org/packages/7a/6c/1c1a7142b69662b802e511c4318d931adbf539827511f62cdb0fc8f7e609/fastapi_jsonapi-2.8.0-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7ce3d9f1a13b79d2871afa330d54ee33572278328e5dd95e8fadcf2ee5793e5d",
                "md5": "d60191d46cb6c269ae1d7a9c2ead3a6e",
                "sha256": "06883e2d846635a1e6149d9307cd4ff397f8169a5ba6978020fbc89a4f6929e4"
            },
            "downloads": -1,
            "filename": "fastapi_jsonapi-2.8.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d60191d46cb6c269ae1d7a9c2ead3a6e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 66759,
            "upload_time": "2024-04-27T13:27:10",
            "upload_time_iso_8601": "2024-04-27T13:27:10.240956Z",
            "url": "https://files.pythonhosted.org/packages/7c/e3/d9f1a13b79d2871afa330d54ee33572278328e5dd95e8fadcf2ee5793e5d/fastapi_jsonapi-2.8.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-27 13:27:10",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mts-ai",
    "github_project": "FastAPI-JSONAPI",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "fastapi-jsonapi"
}
        
Elapsed time: 0.34951s