Skip to main content

File storage for HawkAPI — local, S3, GCS, Azure backends, pre-signed URLs, streaming uploads

Project description

hawkapi-storage

Pluggable file storage for HawkAPI. One Storage protocol, four backends: local filesystem, AWS S3 (and S3-compatible — MinIO, Wasabi, R2), Google Cloud Storage, Azure Blob Storage. Pre-signed URLs and streaming on all of them.

Install

pip install hawkapi-storage                  # local filesystem only
pip install 'hawkapi-storage[s3]'            # + AWS S3
pip install 'hawkapi-storage[gcs]'           # + Google Cloud Storage
pip install 'hawkapi-storage[azure]'         # + Azure Blob Storage

Quickstart

from hawkapi import Depends, HawkAPI
from hawkapi_storage import LocalConfig, LocalStorage, Storage, get_storage, init_storage

app = HawkAPI()
init_storage(app, storage=LocalStorage(LocalConfig(root="/var/data", base_url="https://cdn.example")))


@app.put("/files/{key}")
async def upload(key: str, body: bytes, s: Storage = Depends(get_storage)):
    obj = await s.put(key, body, content_type="application/octet-stream")
    return {"key": obj.key, "size": obj.size}


@app.get("/files/{key}/url")
async def signed(key: str, s: Storage = Depends(get_storage)):
    return {"url": await s.signed_url(key, expires_in=300)}

Swap LocalStorage for any other backend — every primitive is identical.

Backends

from hawkapi_storage import (
    LocalStorage, LocalConfig,
    S3Storage,    S3Config,        # extras: [s3]
    GCSStorage,   GCSConfig,       # extras: [gcs]
    AzureStorage, AzureConfig,     # extras: [azure]
)

local = LocalStorage(LocalConfig(root="/var/data"))
s3    = S3Storage(S3Config(bucket="my-bucket", region="eu-west-1"))
minio = S3Storage(S3Config(bucket="mb", endpoint_url="https://minio.example", use_path_style=True))
gcs   = GCSStorage(GCSConfig(bucket="my-bucket", project="my-project"))
azure = AzureStorage(AzureConfig(container="files", connection_string="..."))

The Storage protocol

class Storage(Protocol):
    name: str

    async def put(self, key, data, *, content_type=None, metadata=None) -> StoredObject: ...
    async def get(self, key) -> bytes: ...
    async def stream(self, key, *, chunk_size=65536) -> AsyncIterator[bytes]: ...
    async def exists(self, key) -> bool: ...
    async def delete(self, key) -> None: ...
    async def head(self, key) -> StoredObject: ...
    async def list(self, prefix="", *, limit=1000) -> AsyncIterator[StoredObject]: ...
    async def signed_url(self, key, *, expires_in=3600, method="GET", content_type=None) -> str: ...

put() accepts bytes, a file-like object, or an AsyncIterator[bytes] (for streaming uploads).

Streaming downloads

@app.get("/download/{key}")
async def download(key: str, s: Storage = Depends(get_storage)):
    return StreamingResponse(s.stream(key, chunk_size=65536),
                             media_type=(await s.head(key)).content_type)

Pre-signed URLs

Every backend supports signed_url(key, expires_in=..., method="GET" | "PUT"). For PUT/upload pre-signs, pass content_type= so the client must send the matching Content-Type header.

LocalStorage produces HMAC-signed URLs that you verify on download with local.verify_signed_url(key, expires, sig, method="GET") — useful when serving downloads through your own handler.

Local filesystem details

  • Path traversal (..) is rejected at put/get time.
  • LocalConfig(base_url=...) sets the prefix used by signed_url() — pair it with a Nginx alias or a HawkAPI download handler.
  • LocalConfig(signing_secret=...) lets you pin the HMAC secret (otherwise generated once at startup).

Errors

  • StorageError — base class.
  • NotFoundError(key)get / head / stream on a missing key.

Development

git clone https://github.com/Hawk-API/hawkapi-storage.git
cd hawkapi-storage
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/

License

MIT.

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

hawkapi_storage-0.2.1.tar.gz (50.6 kB view details)

Uploaded Source

Built Distribution

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

hawkapi_storage-0.2.1-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file hawkapi_storage-0.2.1.tar.gz.

File metadata

  • Download URL: hawkapi_storage-0.2.1.tar.gz
  • Upload date:
  • Size: 50.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hawkapi_storage-0.2.1.tar.gz
Algorithm Hash digest
SHA256 907c3f6199b8ec09bf29f4d3078d9238f7593de545692141bc77ea3a3ba1b872
MD5 6a8f4020ce658957cea12643ad2736e9
BLAKE2b-256 411b577c781c21aef5a65136ecd9739328d3b30bc91ecb668488332b0999f966

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_storage-0.2.1.tar.gz:

Publisher: release.yml on Hawk-API/hawkapi-storage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hawkapi_storage-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: hawkapi_storage-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hawkapi_storage-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5c34daad827e53514a7eb5b87e7dde2866e5be9315e4881dfb4cd3aad233b170
MD5 0549971b0e4b475c1c0d1ab61465b5d3
BLAKE2b-256 0ba3ab89f06b62bc870cb9a875eabd000af399d6aa42f5952b1911d929483286

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_storage-0.2.1-py3-none-any.whl:

Publisher: release.yml on Hawk-API/hawkapi-storage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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