Skip to main content

FastAPI extension to create REST web api according to JSON:API 1.0 specification with FastAPI, Pydantic and data provider (SQLAlchemy)

Project description

Last Commit PyPI GitHub Actions Read the Docs Codecov

📖 Docs (gh-pages)

FastAPI-JSONAPI

FastAPI-JSONAPI is a FastAPI extension for building REST APIs. Implementation of a strong specification JSONAPI 1.0. 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

Install

pip install FastAPI-JSONAPI

A minimal API

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

import sys
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Any, ClassVar, Optional
from typing import Union

import uvicorn
from fastapi import Depends, FastAPI
from fastapi.responses import ORJSONResponse as JSONResponse
from pydantic import ConfigDict
from sqlalchemy.engine import URL
from sqlalchemy.engine import make_url
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

from fastapi_jsonapi import ApplicationBuilder
from fastapi_jsonapi.misc.sqla.generics.base import ViewBaseGeneric
from fastapi_jsonapi.schema_base import BaseModel
from fastapi_jsonapi.views import ViewBase, Operation, OperationConfig

CURRENT_DIR = Path(__file__).resolve().parent
sys.path.append(f"{CURRENT_DIR.parent.parent}")


class DB:
    def __init__(
        self,
        url: Union[str, URL],
        echo: bool = False,
        echo_pool: bool = False,
    ):
        self.engine: AsyncEngine = create_async_engine(
            url=url,
            echo=echo,
            echo_pool=echo_pool,
        )

        self.session_maker: async_sessionmaker[AsyncSession] = async_sessionmaker(
            autocommit=False,
            bind=self.engine,
            expire_on_commit=False,
        )

    async def dispose(self):
        await self.engine.dispose()

    async def session(self) -> AsyncIterator[AsyncSession]:
        async with self.session_maker() as session:
            yield session


db = DB(
    url=make_url(f"sqlite+aiosqlite:///{CURRENT_DIR}/db.sqlite3"),
)


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[Optional[str]]


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

    model_config = ConfigDict(
        from_attributes=True,
    )

    name: str


class SessionDependency(BaseModel):
    model_config = ConfigDict(
        arbitrary_types_allowed=True,
    )

    session: AsyncSession = Depends(db.session)


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


class UserView(ViewBaseGeneric):
    operation_dependencies: ClassVar = {
        Operation.ALL: OperationConfig(
            dependencies=SessionDependency,
            prepare_data_layer_kwargs=session_dependency_handler,
        ),
    }


def add_routes(app: FastAPI):
    builder = ApplicationBuilder(app)
    builder.add_resource(
        path="/users",
        tags=["User"],
        view=UserView,
        schema=UserSchema,
        model=User,
        resource_type="user",
    )
    builder.initialize()


# noinspection PyUnusedLocal
@asynccontextmanager
async def lifespan(app: FastAPI):
    add_routes(app)

    async with db.engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    yield

    await db.dispose()


app = FastAPI(
    title="FastAPI and SQLAlchemy",
    lifespan=lifespan,
    debug=True,
    default_response_class=JSONResponse,
    docs_url="/docs",
    openapi_url="/openapi.json",
)


if __name__ == "__main__":
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8080,
    )

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
/operations/ POST atomic Create, update, delete users

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_jsonapi-3.0.0.tar.gz (53.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_jsonapi-3.0.0-py2.py3-none-any.whl (64.1 kB view details)

Uploaded Python 2Python 3

File details

Details for the file fastapi_jsonapi-3.0.0.tar.gz.

File metadata

  • Download URL: fastapi_jsonapi-3.0.0.tar.gz
  • Upload date:
  • Size: 53.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for fastapi_jsonapi-3.0.0.tar.gz
Algorithm Hash digest
SHA256 0aad95320c7e5ce0b685d12e5bf48c2bd130e43b34d1c9ede8fe2176f0dc5fb8
MD5 0cd0bf8ba6fda069977e9cda31b3317c
BLAKE2b-256 9b182690d07da5e7246fd790433981b30da3053d7e4a5a21eb042a5765ac1618

See more details on using hashes here.

File details

Details for the file fastapi_jsonapi-3.0.0-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_jsonapi-3.0.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 f0ca361181a446ecc2917e15b5b979bbfbcb8d6c2b948b73d6573124cb8e5735
MD5 2f775b5f81b95907eb678e10b80d3713
BLAKE2b-256 a4eec4a67cad01be70fbe25401d88319fa8c16e904814b5a4379e4c4a2bce439

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page