Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
8 changes: 8 additions & 0 deletions docs/source/api_reference.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
API Reference
=============

.. toctree::
:maxdepth: 2

async_storages
async_storages.integrations
10 changes: 10 additions & 0 deletions docs/source/async_storages.integrations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
async_storages.integrations
=========================

.. autoclass:: async_storages.integrations.sqlalchemy.FileType
:exclude-members: cache_ok, impl, process_bind_param, process_result_value
:show-inheritance:

.. autoclass:: async_storages.integrations.sqlalchemy.ImageType
:exclude-members: process_result_value
:show-inheritance:
6 changes: 6 additions & 0 deletions docs/source/async_storages.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
async_storages
=============

.. automodule:: async_storages
:members:
:show-inheritance:
30 changes: 30 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "fastapi-async-storages"
copyright = "2025, stabldev"
author = "stabldev"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = [
# "qiskit_sphinx_theme",
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
]

templates_path = ["_templates"]
exclude_patterns = []

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = "furo"
html_static_path = ["_static"]
53 changes: 53 additions & 0 deletions docs/source/contributing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Contributing
============

Thank you for your interest in contributing to ``fastapi-async-storages``!
We welcome contributions of all kinds; whether code, documentation, bug reports, or ideas.

Contribution Guide
------------------

How to Contribute
~~~~~~~~~~~~~~~~~
1. **Check the roadmap and open issues**

See what’s planned or in progress to avoid duplication and align your efforts.

2. **Fork the repository and create a feature branch**

Create your own copy of the project to work on.
Use a descriptive name for your branch, e.g., ``feature/async-gcs-backend``.

3. **Write clear, concise code and documentation**

Follow existing style and patterns. Include tests for new features or bug fixes.

4. **Open a pull request (PR)**

Provide a detailed description of your changes, related issues, and any testing you performed.

5. **Respond to review feedback**

We may ask for clarifications, improvements, or changes before merging.

Coding Guidelines
~~~~~~~~~~~~~~~~~
- Write asynchronous code following Python’s async/await conventions.
- Ensure compatibility with FastAPI and `SQLAlchemy <https://sqlalchemy.org/>`_/`SQLModel <https://sqlmodel.tiangolo.com/>`_ async patterns.
- Add type hints for functions and methods.
- Document public APIs and complex internal logic clearly.
- Adhere to `PEP 8 style <https://peps.python.org/pep-0008/>`_ guidelines for Python code.

Reporting Issues
~~~~~~~~~~~~~~~~~
- Use the issue tracker to report bugs, suggest enhancements, or ask questions.
- Provide clear reproduction steps, error messages, and environment details.

Community Conduct
~~~~~~~~~~~~~~~~~
- Be respectful and considerate to all contributors.
- Follow inclusive language practices.
- Contributions must comply with the project’s license.

Thank you for helping make ``fastapi-async-storages`` better for everyone!
We look forward to your contributions and collaboration.
39 changes: 39 additions & 0 deletions docs/source/faq.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FAQ
===

This section answers common questions about ``fastapi-async-storages``, explaining its purpose,
differences from similar libraries, usage considerations, and extensibility options.

What is `fastapi-async-storages <https://github.com/stabldev/fastapi-async-storages>`_?
---------------------------------------------------------------------------------------
`fastapi-async-storages <https://github.com/stabldev/fastapi-async-storages>`_ is the asynchronous counterpart to `fastapi-storages <https://github.com/aminalaee/fastapi-storages>`_.
It provides non-blocking storage backends for FastAPI applications using async libraries such as `aioboto3 <https://aioboto3.readthedocs.io/en/latest/usage.html>`_ for S3.

How is it different from `fastapi-storages <https://github.com/aminalaee/fastapi-storages>`_?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While `fastapi-storages <https://github.com/aminalaee/fastapi-storages>`_ is sync-based and built around traditional blocking I/O,
`fastapi-async-storages <https://github.com/stabldev/fastapi-async-storages>`_ is fully asynchronous.
This allows your FastAPI app to handle more concurrent requests efficiently, especially during file upload or download operations.

Why upload files before commit?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`SQLAlchemy <https://sqlalchemy.org>`_’s ORM is primarily synchronous, even when used with `AsyncSession <https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html>`_.
The model layer and column bindings (including file fields) are not async-aware.
Therefore, storage operations like uploading or deleting files must happen **before** the database commit.

Does `SQLModel <https://sqlmodel.tiangolo.com/>`_ integration work the same way?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes. The `SQLModel <https://sqlmodel.tiangolo.com/>`_ integration layer internally uses `SQLAlchemy <https://sqlalchemy.org>`_’s async engine and sessions.
You can define :class:`~async_storages.integrations.sqlalchemy.FileType` columns exactly as you would in SQLAlchemy models, following the same upload-before-commit pattern.

Can I use it in synchronous contexts?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can, but it’s not recommended. The library is designed around `asyncio <https://docs.python.org/3/library/asyncio.html>`_ to take advantage of FastAPI’s asynchronous execution.
If your app runs entirely synchronously, stick with `fastapi-storages <https://github.com/aminalaee/fastapi-storages>`_ for simplicity.

Can I extend it with other storage backends?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes. The library’s base :class:`~async_storages.base.BaseStorage` class is extensible.
You can implement new async backends (like local filesystem, Google Cloud Storage, or Azure Blob) by subclassing it and implementing the async methods ``upload``, ``get_url``, and ``delete``.

`fastapi-async-storages <https://github.com/stabldev/fastapi-async-storages>`_ aims to stay compatible with existing `fastapi-storages <https://github.com/aminalaee/fastapi-storages>`_ APIs, so extension patterns remain familiar.
86 changes: 86 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
fastapi-async-storages
======================

A powerful, extensible, and async-ready cloud object storage backend for FastAPI.

.. epigraph::
Drop-in, plug-and-play cloud storage for your FastAPI apps; with full async support.
Inspired by `fastapi-storages <https://github.com/aminalaee/fastapi-storages>`_,
built on modern async patterns using `aioboto3 <https://github.com/terricain/aioboto3>`_.

Features
--------

* Fully asynchronous storage interface designed for FastAPI applications
* Async S3 backend powered by `aioboto3 <https://github.com/terricain/aioboto3>`_
* `SQLAlchemy <https://sqlalchemy.org/>`_ and `SQLModel <https://sqlmodel.tiangolo.com/>`_ integration
* Typed and extensible design
* Supports FastAPI dependency injection

Table of contents
-----------------

.. toctree::
:caption: USER GUIDE
:maxdepth: 3

installation
usage
api_reference

.. toctree::
:maxdepth: 1

faq

.. toctree::
:caption: DEVELOPMENT
:maxdepth: 1

roadmap
contributing

Example: FastAPI
----------------

.. code-block:: python

from fastapi import FastAPI, UploadFile
from sqlalchemy import Column, Integer
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
from async_storages import S3Storage
from async_storages.integrations.sqlalchemy import FileType

Base = declarative_base()

app = FastAPI()
storage = S3Storage(...)
engine = create_async_engine("sqlite+aiosqlite:///test.db", echo=True)

# create AsyncSession factory
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

class Example(Base):
__tablename__ = "example"

id = Column(Integer, primary_key=True)
file = Column(FileType(storage=storage))

# create tables inside an async context
@app.on_event("startup")
async def startup():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)

@app.post("/upload/")
async def create_upload_file(file: UploadFile):
file_name = f"uploads/{file.filename}"
# upload before commit due to the sqlalchemy binding being sync
await storage.upload(file.file, file_name)

example = Example(file=file)
async with AsyncSessionLocal() as session:
session.add(example)
await session.commit()
return {"filename": example.file.name}
49 changes: 49 additions & 0 deletions docs/source/installation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Installation
============

This document explains how to install the ``fastapi-async-storages`` package.
Note that you can use any package manager you prefer, such as `uv`, `pip`, or others.

Prerequisites
-------------
- Python 3.12 or higher
- A package manager like `uv` installed (see https://astral.sh/uv/ for more info)

Installation with uv
--------------------

You can install the package using `uv`:

.. code-block:: bash

uv add fastapi-async-storages
# for s3 support:
uv add fastapi-async-storages[s3]

.. warning::

If you need **image support** (using :class:`~async_storages.integrations.sqlalchemy.ImageType` or :class:`~async_storages.StorageImage`),
make sure :code:`Pillow` library is installed.

.. code-block:: bash

uv add Pillow
# or uv pip install Pillow

Or, to install from source:

.. code-block:: bash

git clone https://github.com/stabldev/fastapi-async-storages.git
cd fastapi-async-storages
uv sync --all-extras

Verify installation
-------------------

You can verify the package installation by importing it:

.. code-block:: python

import async_storages
print(async_storages.__version__)
36 changes: 36 additions & 0 deletions docs/source/roadmap.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Roadmap
=======

This list shows the current and planned features of ``fastapi-async-storages``;
checked items are implemented, unchecked are upcoming.

Completed & Planned Features
-------------------------------

Storage Backends
~~~~~~~~~~~~~~~~
- [x] Async S3 backend powered by `aioboto3 <https://aioboto3.readthedocs.io/en/latest/>`_
- [x] Compatibility with `MinIO <https://min.io/>`_ and other S3-compatible services
- [ ] Async Local Filesystem backend using `aiofiles <https://github.com/Tinche/aiofiles>`_
- [ ] Async Google Cloud Storage (GCS) backend using `google-cloud-storage <https://googleapis.dev/python/storage/latest/index.html>`_
- [ ] Async Azure Blob Storage integration with `azure-sdk-for-python <https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-python>`_

Framework Integrations
~~~~~~~~~~~~~~~~~~~~~~
- [x] `SQLAlchemy ORM <https://sqlalchemy.org/>`_ async integration
- [ ] `Tortoise ORM <https://tortoise-orm.readthedocs.io/en/latest/>`_ async integration
- [ ] `Peewee ORM <http://docs.peewee-orm.com/en/latest/peewee/async.html>`_ async integration

Features & Enhancements
~~~~~~~~~~~~~~~~~~~~~~~
- [x] Presigned URL generation for uploads and downloads
- [ ] Async streaming support for large files
- [ ] Bulk async upload and delete operations
- [ ] Progress tracking with hooks or callbacks
- [ ] Automatic cleanup utilities for orphaned/expired files

DX & Documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- [x] Core documentation and usage examples
- [x] Testing utilities and mocks for async storage testing
- [ ] Expanded real-world usage and best practices
Loading